XmlFormatWriter.cs 6.3 KB

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