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