blob: 97fc6b2c377bbc74698e4c5b3bc9b4238fd140eb [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 {
csharptest2b868842011-06-10 14:41:47 -050078 _output.Close();
csharptest74c5e0c2011-07-14 13:06:22 -050079 }
csharptest2b868842011-06-10 14:41:47 -050080 }
81
82 /// <summary>
83 /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
84 /// </summary>
85 public string RootElementName
86 {
87 get { return _rootElementName; }
csharptest74c5e0c2011-07-14 13:06:22 -050088 set
89 {
90 ThrowHelper.ThrowIfNull(value, "RootElementName");
91 _rootElementName = value;
92 }
csharptest2b868842011-06-10 14:41:47 -050093 }
94
95 /// <summary>
96 /// Gets or sets the options to use while generating the XML
97 /// </summary>
98 public XmlWriterOptions Options { get; set; }
csharptest74c5e0c2011-07-14 13:06:22 -050099
csharptest7fc785c2011-06-10 23:54:53 -0500100 /// <summary>
101 /// Sets the options to use while generating the XML
102 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500103 public XmlFormatWriter SetOptions(XmlWriterOptions options)
104 {
105 Options = options;
106 return this;
107 }
csharptest2b868842011-06-10 14:41:47 -0500108
csharptest74c5e0c2011-07-14 13:06:22 -0500109 private bool TestOption(XmlWriterOptions option)
110 {
111 return (Options & option) != 0;
112 }
csharptest2b868842011-06-10 14:41:47 -0500113
114 /// <summary>
csharptestc2d2c1a2011-09-08 20:02:11 -0500115 /// Used to write the root-message preamble, in xml this is open element for RootElementName,
116 /// by default "&lt;root&gt;". After this call you can call IMessageLite.MergeTo(...) and
117 /// complete the message with a call to EndMessage().
118 /// </summary>
119 public override void StartMessage()
120 {
121 StartMessage(_rootElementName);
122 }
123
124 /// <summary>
125 /// Used to write the root-message preamble, in xml this is open element for elementName.
126 /// After this call you can call IMessageLite.MergeTo(...) and complete the message with
127 /// a call to EndMessage().
128 /// </summary>
129 public void StartMessage(string elementName)
130 {
131 if (TestOption(XmlWriterOptions.OutputJsonTypes))
132 {
133 _output.WriteStartElement("root"); // json requires this is the root-element
134 _output.WriteAttributeString("type", "object");
135 }
136 else
137 {
138 _output.WriteStartElement(elementName);
139 }
140 }
141
142 /// <summary>
143 /// Used to complete a root-message previously started with a call to StartMessage()
144 /// </summary>
145 public override void EndMessage()
146 {
147 _output.WriteEndElement();
148 _output.Flush();
149 }
150
151 /// <summary>
csharptest2b868842011-06-10 14:41:47 -0500152 /// Writes a message as an element using the name defined in <see cref="RootElementName"/>
153 /// </summary>
154 public override void WriteMessage(IMessageLite message)
csharptest74c5e0c2011-07-14 13:06:22 -0500155 {
156 WriteMessage(_rootElementName, message);
157 }
csharptest2b868842011-06-10 14:41:47 -0500158
159 /// <summary>
160 /// Writes a message as an element with the given name
161 /// </summary>
csharptest4dc0dfb2011-06-10 18:01:34 -0500162 public void WriteMessage(string elementName, IMessageLite message)
csharptest2b868842011-06-10 14:41:47 -0500163 {
csharptestc2d2c1a2011-09-08 20:02:11 -0500164 StartMessage(elementName);
csharptest2b868842011-06-10 14:41:47 -0500165 message.WriteTo(this);
csharptestc2d2c1a2011-09-08 20:02:11 -0500166 EndMessage();
csharptest2b868842011-06-10 14:41:47 -0500167 }
168
169 /// <summary>
170 /// Writes a message
171 /// </summary>
172 protected override void WriteMessageOrGroup(string field, IMessageLite message)
173 {
174 _output.WriteStartElement(field);
175
176 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500177 {
csharptest2b868842011-06-10 14:41:47 -0500178 _output.WriteAttributeString("type", "object");
csharptest74c5e0c2011-07-14 13:06:22 -0500179 }
csharptest2b868842011-06-10 14:41:47 -0500180
181 message.WriteTo(this);
182 _output.WriteEndElement();
183 }
184
185 /// <summary>
186 /// Writes a String value
187 /// </summary>
188 protected override void WriteAsText(string field, string textValue, object typedValue)
189 {
190 _output.WriteStartElement(field);
191
192 if (TestOption(XmlWriterOptions.OutputJsonTypes))
193 {
csharptest74c5e0c2011-07-14 13:06:22 -0500194 if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong ||
195 typedValue is double || typedValue is float)
196 {
csharptest2b868842011-06-10 14:41:47 -0500197 _output.WriteAttributeString("type", "number");
csharptest74c5e0c2011-07-14 13:06:22 -0500198 }
csharptest2b868842011-06-10 14:41:47 -0500199 else if (typedValue is bool)
csharptest74c5e0c2011-07-14 13:06:22 -0500200 {
csharptest2b868842011-06-10 14:41:47 -0500201 _output.WriteAttributeString("type", "boolean");
csharptest74c5e0c2011-07-14 13:06:22 -0500202 }
csharptest2b868842011-06-10 14:41:47 -0500203 }
204 _output.WriteString(textValue);
205
206 //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
207 if (_output.WriteState == WriteState.Element)
csharptest74c5e0c2011-07-14 13:06:22 -0500208 {
csharptest2b868842011-06-10 14:41:47 -0500209 _output.WriteRaw("");
csharptest74c5e0c2011-07-14 13:06:22 -0500210 }
csharptest2b868842011-06-10 14:41:47 -0500211
212 _output.WriteEndElement();
213 }
214
215 /// <summary>
216 /// Writes an array of field values
217 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500218 protected override void WriteArray(FieldType fieldType, string field, IEnumerable items)
csharptest2b868842011-06-10 14:41:47 -0500219 {
220 //see if it's empty
csharptest74c5e0c2011-07-14 13:06:22 -0500221 IEnumerator eitems = items.GetEnumerator();
222 try
223 {
224 if (!eitems.MoveNext())
225 {
226 return;
227 }
228 }
csharptest2b868842011-06-10 14:41:47 -0500229 finally
csharptest74c5e0c2011-07-14 13:06:22 -0500230 {
231 if (eitems is IDisposable)
232 {
233 ((IDisposable) eitems).Dispose();
234 }
235 }
csharptest2b868842011-06-10 14:41:47 -0500236
237 if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
238 {
239 _output.WriteStartElement(field);
240 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500241 {
csharptest2b868842011-06-10 14:41:47 -0500242 _output.WriteAttributeString("type", "array");
csharptest74c5e0c2011-07-14 13:06:22 -0500243 }
csharptest2b868842011-06-10 14:41:47 -0500244
245 base.WriteArray(fieldType, "item", items);
246 _output.WriteEndElement();
247 }
248 else
csharptest74c5e0c2011-07-14 13:06:22 -0500249 {
csharptest2b868842011-06-10 14:41:47 -0500250 base.WriteArray(fieldType, field, items);
csharptest74c5e0c2011-07-14 13:06:22 -0500251 }
csharptest2b868842011-06-10 14:41:47 -0500252 }
253
254 /// <summary>
255 /// Writes a System.Enum by the numeric and textual value
256 /// </summary>
257 protected override void WriteEnum(string field, int number, string name)
258 {
259 _output.WriteStartElement(field);
260
261 if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
csharptest74c5e0c2011-07-14 13:06:22 -0500262 {
csharptest2b868842011-06-10 14:41:47 -0500263 _output.WriteAttributeString("value", XmlConvert.ToString(number));
csharptest74c5e0c2011-07-14 13:06:22 -0500264 }
csharptest2b868842011-06-10 14:41:47 -0500265
266 _output.WriteString(name);
267 _output.WriteEndElement();
268 }
269 }
csharptest74c5e0c2011-07-14 13:06:22 -0500270}