blob: fd36c1de01c29d0a175bab096ed5fa8b05f37391 [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>
115 /// Writes a message as an element using the name defined in <see cref="RootElementName"/>
116 /// </summary>
117 public override void WriteMessage(IMessageLite message)
csharptest74c5e0c2011-07-14 13:06:22 -0500118 {
119 WriteMessage(_rootElementName, message);
120 }
csharptest2b868842011-06-10 14:41:47 -0500121
122 /// <summary>
123 /// Writes a message as an element with the given name
124 /// </summary>
csharptest4dc0dfb2011-06-10 18:01:34 -0500125 public void WriteMessage(string elementName, IMessageLite message)
csharptest2b868842011-06-10 14:41:47 -0500126 {
127 if (TestOption(XmlWriterOptions.OutputJsonTypes))
128 {
129 _output.WriteStartElement("root"); // json requires this is the root-element
130 _output.WriteAttributeString("type", "object");
131 }
132 else
csharptest74c5e0c2011-07-14 13:06:22 -0500133 {
csharptest2b868842011-06-10 14:41:47 -0500134 _output.WriteStartElement(elementName);
csharptest74c5e0c2011-07-14 13:06:22 -0500135 }
csharptest2b868842011-06-10 14:41:47 -0500136
137 message.WriteTo(this);
138 _output.WriteEndElement();
139 _output.Flush();
140 }
141
142 /// <summary>
143 /// Writes a message
144 /// </summary>
145 protected override void WriteMessageOrGroup(string field, IMessageLite message)
146 {
147 _output.WriteStartElement(field);
148
149 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500150 {
csharptest2b868842011-06-10 14:41:47 -0500151 _output.WriteAttributeString("type", "object");
csharptest74c5e0c2011-07-14 13:06:22 -0500152 }
csharptest2b868842011-06-10 14:41:47 -0500153
154 message.WriteTo(this);
155 _output.WriteEndElement();
156 }
157
158 /// <summary>
159 /// Writes a String value
160 /// </summary>
161 protected override void WriteAsText(string field, string textValue, object typedValue)
162 {
163 _output.WriteStartElement(field);
164
165 if (TestOption(XmlWriterOptions.OutputJsonTypes))
166 {
csharptest74c5e0c2011-07-14 13:06:22 -0500167 if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong ||
168 typedValue is double || typedValue is float)
169 {
csharptest2b868842011-06-10 14:41:47 -0500170 _output.WriteAttributeString("type", "number");
csharptest74c5e0c2011-07-14 13:06:22 -0500171 }
csharptest2b868842011-06-10 14:41:47 -0500172 else if (typedValue is bool)
csharptest74c5e0c2011-07-14 13:06:22 -0500173 {
csharptest2b868842011-06-10 14:41:47 -0500174 _output.WriteAttributeString("type", "boolean");
csharptest74c5e0c2011-07-14 13:06:22 -0500175 }
csharptest2b868842011-06-10 14:41:47 -0500176 }
177 _output.WriteString(textValue);
178
179 //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
180 if (_output.WriteState == WriteState.Element)
csharptest74c5e0c2011-07-14 13:06:22 -0500181 {
csharptest2b868842011-06-10 14:41:47 -0500182 _output.WriteRaw("");
csharptest74c5e0c2011-07-14 13:06:22 -0500183 }
csharptest2b868842011-06-10 14:41:47 -0500184
185 _output.WriteEndElement();
186 }
187
188 /// <summary>
189 /// Writes an array of field values
190 /// </summary>
csharptest74c5e0c2011-07-14 13:06:22 -0500191 protected override void WriteArray(FieldType fieldType, string field, IEnumerable items)
csharptest2b868842011-06-10 14:41:47 -0500192 {
193 //see if it's empty
csharptest74c5e0c2011-07-14 13:06:22 -0500194 IEnumerator eitems = items.GetEnumerator();
195 try
196 {
197 if (!eitems.MoveNext())
198 {
199 return;
200 }
201 }
csharptest2b868842011-06-10 14:41:47 -0500202 finally
csharptest74c5e0c2011-07-14 13:06:22 -0500203 {
204 if (eitems is IDisposable)
205 {
206 ((IDisposable) eitems).Dispose();
207 }
208 }
csharptest2b868842011-06-10 14:41:47 -0500209
210 if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
211 {
212 _output.WriteStartElement(field);
213 if (TestOption(XmlWriterOptions.OutputJsonTypes))
csharptest74c5e0c2011-07-14 13:06:22 -0500214 {
csharptest2b868842011-06-10 14:41:47 -0500215 _output.WriteAttributeString("type", "array");
csharptest74c5e0c2011-07-14 13:06:22 -0500216 }
csharptest2b868842011-06-10 14:41:47 -0500217
218 base.WriteArray(fieldType, "item", items);
219 _output.WriteEndElement();
220 }
221 else
csharptest74c5e0c2011-07-14 13:06:22 -0500222 {
csharptest2b868842011-06-10 14:41:47 -0500223 base.WriteArray(fieldType, field, items);
csharptest74c5e0c2011-07-14 13:06:22 -0500224 }
csharptest2b868842011-06-10 14:41:47 -0500225 }
226
227 /// <summary>
228 /// Writes a System.Enum by the numeric and textual value
229 /// </summary>
230 protected override void WriteEnum(string field, int number, string name)
231 {
232 _output.WriteStartElement(field);
233
234 if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
csharptest74c5e0c2011-07-14 13:06:22 -0500235 {
csharptest2b868842011-06-10 14:41:47 -0500236 _output.WriteAttributeString("value", XmlConvert.ToString(number));
csharptest74c5e0c2011-07-14 13:06:22 -0500237 }
csharptest2b868842011-06-10 14:41:47 -0500238
239 _output.WriteString(name);
240 _output.WriteEndElement();
241 }
242 }
csharptest74c5e0c2011-07-14 13:06:22 -0500243}