blob: a9cfcc1e2d73c06a0fb92807198b56e7523c004f [file] [log] [blame]
csharptest2b868842011-06-10 14:41:47 -05001using System;
csharptest74c5e0c2011-07-14 13:06:22 -05002using System.Collections;
csharptest2b868842011-06-10 14:41:47 -05003using System.IO;
csharptest7fc785c2011-06-10 23:54:53 -05004using System.Text;
csharptest2b868842011-06-10 14:41:47 -05005using System.Xml;
6using Google.ProtocolBuffers.Descriptors;
7
8namespace Google.ProtocolBuffers.Serialization
9{
10 /// <summary>
11 /// Writes a proto buffer to an XML document or fragment. .NET 3.5 users may also
12 /// use this class to produce Json by setting the options to support Json and providing
13 /// an XmlWriter obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>.
14 /// </summary>
15 public class XmlFormatWriter : AbstractTextWriter
16 {
17 public const string DefaultRootElementName = "root";
18 private const int NestedArrayFlag = 0x0001;
19 private readonly XmlWriter _output;
20 private string _rootElementName;
21
csharptest74c5e0c2011-07-14 13:06:22 -050022 private static XmlWriterSettings DefaultSettings(Encoding encoding)
csharptest2b868842011-06-10 14:41:47 -050023 {
csharptest74c5e0c2011-07-14 13:06:22 -050024 return new XmlWriterSettings()
25 {
26 CheckCharacters = false,
27 NewLineHandling = NewLineHandling.Entitize,
28 OmitXmlDeclaration = true,
29 Encoding = encoding,
30 };
csharptest2b868842011-06-10 14:41:47 -050031 }
32
33 /// <summary>
34 /// Constructs the XmlFormatWriter to write to the given TextWriter
35 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050036 public static XmlFormatWriter CreateInstance(TextWriter output)
37 {
38 return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(output.Encoding)));
39 }
40
csharptest2b868842011-06-10 14:41:47 -050041 /// <summary>
42 /// Constructs the XmlFormatWriter to write to the given stream
43 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050044 public static XmlFormatWriter CreateInstance(Stream output)
45 {
46 return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(Encoding.UTF8)));
47 }
48
csharptest7fc785c2011-06-10 23:54:53 -050049 /// <summary>
50 /// Constructs the XmlFormatWriter to write to the given stream
51 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050052 public static XmlFormatWriter CreateInstance(Stream output, Encoding encoding)
53 {
54 return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(encoding)));
55 }
56
csharptest2b868842011-06-10 14:41:47 -050057 /// <summary>
58 /// Constructs the XmlFormatWriter to write to the given XmlWriter
59 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -050060 public static XmlFormatWriter CreateInstance(XmlWriter output)
61 {
62 return new XmlFormatWriter(output);
63 }
csharptest7fc785c2011-06-10 23:54:53 -050064
65 protected XmlFormatWriter(XmlWriter output)
csharptest2b868842011-06-10 14:41:47 -050066 {
67 _output = output;
68 _rootElementName = DefaultRootElementName;
69 }
70
71 /// <summary>
72 /// Closes the underlying XmlTextWriter
73 /// </summary>
74 protected override void Dispose(bool disposing)
75 {
csharptest74c5e0c2011-07-14 13:06:22 -050076 if (disposing)
77 {
csharptest819b7152011-09-08 20:28:22 -050078 if (_output.WriteState != WriteState.Closed && _output.WriteState != WriteState.Start)
79 _output.WriteEndDocument();
80
csharptest2b868842011-06-10 14:41:47 -050081 _output.Close();
csharptest74c5e0c2011-07-14 13:06:22 -050082 }
csharptest819b7152011-09-08 20:28:22 -050083
84 base.Dispose(disposing);
csharptest2b868842011-06-10 14:41:47 -050085 }
86
87 /// <summary>
88 /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
89 /// </summary>
90 public string RootElementName
91 {
92 get { return _rootElementName; }
csharptest74c5e0c2011-07-14 13:06:22 -050093 set
94 {
95 ThrowHelper.ThrowIfNull(value, "RootElementName");
96 _rootElementName = value;
97 }
csharptest2b868842011-06-10 14:41:47 -050098 }
99
100 /// <summary>
101 /// Gets or sets the options to use while generating the XML
102 /// </summary>
103 public XmlWriterOptions Options { get; set; }
csharptest74c5e0c2011-07-14 13:06:22 -0500104
csharptest7fc785c2011-06-10 23:54:53 -0500105 /// <summary>
106 /// Sets the options to use while generating the XML
107 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500108 public XmlFormatWriter SetOptions(XmlWriterOptions options)
109 {
110 Options = options;
111 return this;
112 }
csharptest2b868842011-06-10 14:41:47 -0500113
csharptest74c5e0c2011-07-14 13:06:22 -0500114 private bool TestOption(XmlWriterOptions option)
115 {
116 return (Options & option) != 0;
117 }
csharptest2b868842011-06-10 14:41:47 -0500118
119 /// <summary>
csharptest819b7152011-09-08 20:28:22 -0500120 /// Completes any pending write operations
121 /// </summary>
122 public override void Flush()
123 {
124 _output.Flush();
125 base.Flush();
126 }
127
128 /// <summary>
csharptestc2d2c1a2011-09-08 20:02:11 -0500129 /// Used to write the root-message preamble, in xml this is open element for RootElementName,
130 /// by default "&lt;root&gt;". After this call you can call IMessageLite.MergeTo(...) and
131 /// complete the message with a call to EndMessage().
132 /// </summary>
133 public override void StartMessage()
134 {
135 StartMessage(_rootElementName);
136 }
137
138 /// <summary>
139 /// Used to write the root-message preamble, in xml this is open element for elementName.
140 /// After this call you can call IMessageLite.MergeTo(...) and complete the message with
141 /// a call to EndMessage().
142 /// </summary>
143 public void StartMessage(string elementName)
144 {
145 if (TestOption(XmlWriterOptions.OutputJsonTypes))
146 {
147 _output.WriteStartElement("root"); // json requires this is the root-element
148 _output.WriteAttributeString("type", "object");
149 }
150 else
151 {
152 _output.WriteStartElement(elementName);
153 }
154 }
155
156 /// <summary>
157 /// Used to complete a root-message previously started with a call to StartMessage()
158 /// </summary>
159 public override void EndMessage()
160 {
161 _output.WriteEndElement();
162 _output.Flush();
163 }
164
165 /// <summary>
csharptest2b868842011-06-10 14:41:47 -0500166 /// Writes a message as an element using the name defined in <see cref="RootElementName"/>
167 /// </summary>
168 public override void WriteMessage(IMessageLite message)
csharptest74c5e0c2011-07-14 13:06:22 -0500169 {
170 WriteMessage(_rootElementName, message);
171 }
csharptest2b868842011-06-10 14:41:47 -0500172
173 /// <summary>
174 /// Writes a message as an element with the given name
175 /// </summary>
csharptest4dc0dfb2011-06-10 18:01:34 -0500176 public void WriteMessage(string elementName, IMessageLite message)
csharptest2b868842011-06-10 14:41:47 -0500177 {
csharptestc2d2c1a2011-09-08 20:02:11 -0500178 StartMessage(elementName);
csharptest2b868842011-06-10 14:41:47 -0500179 message.WriteTo(this);
csharptestc2d2c1a2011-09-08 20:02:11 -0500180 EndMessage();
csharptest2b868842011-06-10 14:41:47 -0500181 }
182
183 /// <summary>
184 /// Writes a message
185 /// </summary>
186 protected override void WriteMessageOrGroup(string field, IMessageLite message)
187 {
188 _output.WriteStartElement(field);
189
190 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500191 {
csharptest2b868842011-06-10 14:41:47 -0500192 _output.WriteAttributeString("type", "object");
csharptest74c5e0c2011-07-14 13:06:22 -0500193 }
csharptest2b868842011-06-10 14:41:47 -0500194
195 message.WriteTo(this);
196 _output.WriteEndElement();
197 }
198
199 /// <summary>
200 /// Writes a String value
201 /// </summary>
202 protected override void WriteAsText(string field, string textValue, object typedValue)
203 {
204 _output.WriteStartElement(field);
205
206 if (TestOption(XmlWriterOptions.OutputJsonTypes))
207 {
csharptest74c5e0c2011-07-14 13:06:22 -0500208 if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong ||
209 typedValue is double || typedValue is float)
210 {
csharptest2b868842011-06-10 14:41:47 -0500211 _output.WriteAttributeString("type", "number");
csharptest74c5e0c2011-07-14 13:06:22 -0500212 }
csharptest2b868842011-06-10 14:41:47 -0500213 else if (typedValue is bool)
csharptest74c5e0c2011-07-14 13:06:22 -0500214 {
csharptest2b868842011-06-10 14:41:47 -0500215 _output.WriteAttributeString("type", "boolean");
csharptest74c5e0c2011-07-14 13:06:22 -0500216 }
csharptest2b868842011-06-10 14:41:47 -0500217 }
218 _output.WriteString(textValue);
219
220 //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
221 if (_output.WriteState == WriteState.Element)
csharptest74c5e0c2011-07-14 13:06:22 -0500222 {
csharptest2b868842011-06-10 14:41:47 -0500223 _output.WriteRaw("");
csharptest74c5e0c2011-07-14 13:06:22 -0500224 }
csharptest2b868842011-06-10 14:41:47 -0500225
226 _output.WriteEndElement();
227 }
228
229 /// <summary>
230 /// Writes an array of field values
231 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500232 protected override void WriteArray(FieldType fieldType, string field, IEnumerable items)
csharptest2b868842011-06-10 14:41:47 -0500233 {
234 //see if it's empty
csharptest74c5e0c2011-07-14 13:06:22 -0500235 IEnumerator eitems = items.GetEnumerator();
236 try
237 {
238 if (!eitems.MoveNext())
239 {
240 return;
241 }
242 }
csharptest2b868842011-06-10 14:41:47 -0500243 finally
csharptest74c5e0c2011-07-14 13:06:22 -0500244 {
245 if (eitems is IDisposable)
246 {
247 ((IDisposable) eitems).Dispose();
248 }
249 }
csharptest2b868842011-06-10 14:41:47 -0500250
251 if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
252 {
253 _output.WriteStartElement(field);
254 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500255 {
csharptest2b868842011-06-10 14:41:47 -0500256 _output.WriteAttributeString("type", "array");
csharptest74c5e0c2011-07-14 13:06:22 -0500257 }
csharptest2b868842011-06-10 14:41:47 -0500258
259 base.WriteArray(fieldType, "item", items);
260 _output.WriteEndElement();
261 }
262 else
csharptest74c5e0c2011-07-14 13:06:22 -0500263 {
csharptest2b868842011-06-10 14:41:47 -0500264 base.WriteArray(fieldType, field, items);
csharptest74c5e0c2011-07-14 13:06:22 -0500265 }
csharptest2b868842011-06-10 14:41:47 -0500266 }
267
268 /// <summary>
269 /// Writes a System.Enum by the numeric and textual value
270 /// </summary>
271 protected override void WriteEnum(string field, int number, string name)
272 {
273 _output.WriteStartElement(field);
274
275 if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
csharptest74c5e0c2011-07-14 13:06:22 -0500276 {
csharptest2b868842011-06-10 14:41:47 -0500277 _output.WriteAttributeString("value", XmlConvert.ToString(number));
csharptest74c5e0c2011-07-14 13:06:22 -0500278 }
csharptest2b868842011-06-10 14:41:47 -0500279
280 _output.WriteString(name);
281 _output.WriteEndElement();
282 }
283 }
csharptest74c5e0c2011-07-14 13:06:22 -0500284}