blob: 1be2f390e48ddb094d57016a5c014ba0d877edbf [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 {
csharptestf2925232011-06-11 10:41:57 -050023 return new XmlWriterSettings()
24 {
25 CheckCharacters = false,
26 NewLineHandling = NewLineHandling.Entitize,
27 OmitXmlDeclaration = true,
28 Encoding = encoding,
29 };
csharptest2b868842011-06-10 14:41:47 -050030 }
31
32 /// <summary>
33 /// Constructs the XmlFormatWriter to write to the given TextWriter
34 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050035 public static XmlFormatWriter CreateInstance(TextWriter output) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(output.Encoding))); }
csharptest2b868842011-06-10 14:41:47 -050036 /// <summary>
37 /// Constructs the XmlFormatWriter to write to the given stream
38 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050039 public static XmlFormatWriter CreateInstance(Stream output) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(Encoding.UTF8))); }
40 /// <summary>
41 /// Constructs the XmlFormatWriter to write to the given stream
42 /// </summary>
43 public static XmlFormatWriter CreateInstance(Stream output, Encoding encoding) { return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(encoding))); }
csharptest2b868842011-06-10 14:41:47 -050044 /// <summary>
45 /// Constructs the XmlFormatWriter to write to the given XmlWriter
46 /// </summary>
csharptest7fc785c2011-06-10 23:54:53 -050047 public static XmlFormatWriter CreateInstance(XmlWriter output) { return new XmlFormatWriter(output); }
48
49 protected XmlFormatWriter(XmlWriter output)
csharptest2b868842011-06-10 14:41:47 -050050 {
51 _output = output;
52 _rootElementName = DefaultRootElementName;
53 }
54
55 /// <summary>
56 /// Closes the underlying XmlTextWriter
57 /// </summary>
58 protected override void Dispose(bool disposing)
59 {
60 if(disposing)
61 _output.Close();
62 }
63
64 /// <summary>
65 /// Gets or sets the default element name to use when using the Merge&lt;TBuilder>()
66 /// </summary>
67 public string RootElementName
68 {
69 get { return _rootElementName; }
70 set { ThrowHelper.ThrowIfNull(value, "RootElementName"); _rootElementName = value; }
71 }
72
73 /// <summary>
74 /// Gets or sets the options to use while generating the XML
75 /// </summary>
76 public XmlWriterOptions Options { get; set; }
csharptest7fc785c2011-06-10 23:54:53 -050077 /// <summary>
78 /// Sets the options to use while generating the XML
79 /// </summary>
80 public XmlFormatWriter SetOptions(XmlWriterOptions options) { Options = options; return this; }
csharptest2b868842011-06-10 14:41:47 -050081
82 private bool TestOption(XmlWriterOptions option) { return (Options & option) != 0; }
83
84 /// <summary>
85 /// Writes a message as an element using the name defined in <see cref="RootElementName"/>
86 /// </summary>
87 public override void WriteMessage(IMessageLite message)
88 { WriteMessage(_rootElementName, message); }
89
90 /// <summary>
91 /// Writes a message as an element with the given name
92 /// </summary>
csharptest4dc0dfb2011-06-10 18:01:34 -050093 public void WriteMessage(string elementName, IMessageLite message)
csharptest2b868842011-06-10 14:41:47 -050094 {
95 if (TestOption(XmlWriterOptions.OutputJsonTypes))
96 {
97 _output.WriteStartElement("root"); // json requires this is the root-element
98 _output.WriteAttributeString("type", "object");
99 }
100 else
101 _output.WriteStartElement(elementName);
102
103 message.WriteTo(this);
104 _output.WriteEndElement();
105 _output.Flush();
106 }
107
108 /// <summary>
109 /// Writes a message
110 /// </summary>
111 protected override void WriteMessageOrGroup(string field, IMessageLite message)
112 {
113 _output.WriteStartElement(field);
114
115 if (TestOption(XmlWriterOptions.OutputJsonTypes))
116 _output.WriteAttributeString("type", "object");
117
118 message.WriteTo(this);
119 _output.WriteEndElement();
120 }
121
122 /// <summary>
123 /// Writes a String value
124 /// </summary>
125 protected override void WriteAsText(string field, string textValue, object typedValue)
126 {
127 _output.WriteStartElement(field);
128
129 if (TestOption(XmlWriterOptions.OutputJsonTypes))
130 {
131 if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong || typedValue is double || typedValue is float)
132 _output.WriteAttributeString("type", "number");
133 else if (typedValue is bool)
134 _output.WriteAttributeString("type", "boolean");
135 }
136 _output.WriteString(textValue);
137
138 //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>'
139 if (_output.WriteState == WriteState.Element)
140 _output.WriteRaw("");
141
142 _output.WriteEndElement();
143 }
144
145 /// <summary>
146 /// Writes an array of field values
147 /// </summary>
148 protected override void WriteArray(FieldType fieldType, string field, System.Collections.IEnumerable items)
149 {
150 //see if it's empty
151 System.Collections.IEnumerator eitems = items.GetEnumerator();
152 try { if (!eitems.MoveNext()) return; }
153 finally
154 { if (eitems is IDisposable) ((IDisposable) eitems).Dispose(); }
155
156 if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes))
157 {
158 _output.WriteStartElement(field);
159 if (TestOption(XmlWriterOptions.OutputJsonTypes))
160 _output.WriteAttributeString("type", "array");
161
162 base.WriteArray(fieldType, "item", items);
163 _output.WriteEndElement();
164 }
165 else
166 base.WriteArray(fieldType, field, items);
167 }
168
169 /// <summary>
170 /// Writes a System.Enum by the numeric and textual value
171 /// </summary>
172 protected override void WriteEnum(string field, int number, string name)
173 {
174 _output.WriteStartElement(field);
175
176 if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues))
177 _output.WriteAttributeString("value", XmlConvert.ToString(number));
178
179 _output.WriteString(name);
180 _output.WriteEndElement();
181 }
182 }
183}