blob: 2bb4f15930db9b86573fdcdf2f5cda77044ab86b [file] [log] [blame]
csharptest2b868842011-06-10 14:41:47 -05001using System;
2using System.IO;
csharptest7fc785c2011-06-10 23:54:53 -05003using System.Text;
csharptest2b868842011-06-10 14:41:47 -05004using System.Xml;
5using Google.ProtocolBuffers.Descriptors;
6
7namespace Google.ProtocolBuffers.Serialization
8{
9 /// <summary>
10 /// Writes a proto buffer to an XML document or fragment. .NET 3.5 users may also
11 /// use this class to produce Json by setting the options to support Json and providing
12 /// an XmlWriter obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>.
13 /// </summary>
14 public class XmlFormatWriter : AbstractTextWriter
15 {
16 public const string DefaultRootElementName = "root";
17 private const int NestedArrayFlag = 0x0001;
18 private readonly XmlWriter _output;
19 private string _rootElementName;
20
csharptest7fc785c2011-06-10 23:54:53 -050021 static XmlWriterSettings DefaultSettings(Encoding encoding)
csharptest2b868842011-06-10 14:41:47 -050022 {
csharptest7fc785c2011-06-10 23:54:53 -050023 return new XmlWriterSettings() { CheckCharacters = false, NewLineHandling = NewLineHandling.Entitize, Encoding = encoding };
csharptest2b868842011-06-10 14:41:47 -050024 }
25
26 /// <summary>
27 /// Constructs the XmlFormatWriter to write to the given TextWriter
28 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050029 public static XmlFormatWriter CreateInstance(TextWriter output) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(output.Encoding))); }
csharptest2b868842011-06-10 14:41:47 -050030 /// <summary>
31 /// Constructs the XmlFormatWriter to write to the given stream
32 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050033 public static XmlFormatWriter CreateInstance(Stream output) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(Encoding.UTF8))); }
34 /// <summary>
35 /// Constructs the XmlFormatWriter to write to the given stream
36 /// </summary>
37 public static XmlFormatWriter CreateInstance(Stream output, Encoding encoding) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(encoding))); }
csharptest2b868842011-06-10 14:41:47 -050038 /// <summary>
39 /// Constructs the XmlFormatWriter to write to the given XmlWriter
40 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050041 public static XmlFormatWriter CreateInstance(XmlWriter output) { return new XmlFormatWriter(output); }
42
43 protected XmlFormatWriter(XmlWriter output)
csharptest2b868842011-06-10 14:41:47 -050044 {
45 _output = output;
46 _rootElementName = DefaultRootElementName;
47 }
48
49 /// <summary>
50 /// Closes the underlying XmlTextWriter
51 /// </summary>
52 protected override void Dispose(bool disposing)
53 {
54 if(disposing)
55 _output.Close();
56 }
57
58 /// <summary>
59 /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
60 /// </summary>
61 public string RootElementName
62 {
63 get { return _rootElementName; }
64 set { ThrowHelper.ThrowIfNull(value, "RootElementName"); _rootElementName = value; }
65 }
66
67 /// <summary>
68 /// Gets or sets the options to use while generating the XML
69 /// </summary>
70 public XmlWriterOptions Options { get; set; }
csharptest7fc785c2011-06-10 23:54:53 -050071 /// <summary>
72 /// Sets the options to use while generating the XML
73 /// </summary>
74 public XmlFormatWriter SetOptions(XmlWriterOptions options) { Options = options; return this; }
csharptest2b868842011-06-10 14:41:47 -050075
76 private bool TestOption(XmlWriterOptions option) { return (Options & option) != 0; }
77
78 /// <summary>
79 /// Writes a message as an element using the name defined in <see cref="RootElementName"/>
80 /// </summary>
81 public override void WriteMessage(IMessageLite message)
82 { WriteMessage(_rootElementName, message); }
83
84 /// <summary>
85 /// Writes a message as an element with the given name
86 /// </summary>
csharptest4dc0dfb2011-06-10 18:01:34 -050087 public void WriteMessage(string elementName, IMessageLite message)
csharptest2b868842011-06-10 14:41:47 -050088 {
89 if (TestOption(XmlWriterOptions.OutputJsonTypes))
90 {
91 _output.WriteStartElement("root"); // json requires this is the root-element
92 _output.WriteAttributeString("type", "object");
93 }
94 else
95 _output.WriteStartElement(elementName);
96
97 message.WriteTo(this);
98 _output.WriteEndElement();
99 _output.Flush();
100 }
101
102 /// <summary>
103 /// Writes a message
104 /// </summary>
105 protected override void WriteMessageOrGroup(string field, IMessageLite message)
106 {
107 _output.WriteStartElement(field);
108
109 if (TestOption(XmlWriterOptions.OutputJsonTypes))
110 _output.WriteAttributeString("type", "object");
111
112 message.WriteTo(this);
113 _output.WriteEndElement();
114 }
115
116 /// <summary>
117 /// Writes a String value
118 /// </summary>
119 protected override void WriteAsText(string field, string textValue, object typedValue)
120 {
121 _output.WriteStartElement(field);
122
123 if (TestOption(XmlWriterOptions.OutputJsonTypes))
124 {
125 if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong || typedValue is double || typedValue is float)
126 _output.WriteAttributeString("type", "number");
127 else if (typedValue is bool)
128 _output.WriteAttributeString("type", "boolean");
129 }
130 _output.WriteString(textValue);
131
132 //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
133 if (_output.WriteState == WriteState.Element)
134 _output.WriteRaw("");
135
136 _output.WriteEndElement();
137 }
138
139 /// <summary>
140 /// Writes an array of field values
141 /// </summary>
142 protected override void WriteArray(FieldType fieldType, string field, System.Collections.IEnumerable items)
143 {
144 //see if it's empty
145 System.Collections.IEnumerator eitems = items.GetEnumerator();
146 try { if (!eitems.MoveNext()) return; }
147 finally
148 { if (eitems is IDisposable) ((IDisposable) eitems).Dispose(); }
149
150 if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
151 {
152 _output.WriteStartElement(field);
153 if (TestOption(XmlWriterOptions.OutputJsonTypes))
154 _output.WriteAttributeString("type", "array");
155
156 base.WriteArray(fieldType, "item", items);
157 _output.WriteEndElement();
158 }
159 else
160 base.WriteArray(fieldType, field, items);
161 }
162
163 /// <summary>
164 /// Writes a System.Enum by the numeric and textual value
165 /// </summary>
166 protected override void WriteEnum(string field, int number, string name)
167 {
168 _output.WriteStartElement(field);
169
170 if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
171 _output.WriteAttributeString("value", XmlConvert.ToString(number));
172
173 _output.WriteString(name);
174 _output.WriteEndElement();
175 }
176 }
177}