| using System; | |
| using System.Collections; | |
| using System.IO; | |
| using System.Text; | |
| using System.Xml; | |
| using Google.ProtocolBuffers.Descriptors; | |
| namespace Google.ProtocolBuffers.Serialization | |
| { | |
| /// <summary> | |
| /// Writes a proto buffer to an XML document or fragment. .NET 3.5 users may also | |
| /// use this class to produce Json by setting the options to support Json and providing | |
| /// an XmlWriter obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>. | |
| /// </summary> | |
| public class XmlFormatWriter : AbstractTextWriter | |
| { | |
| private static readonly Encoding DefaultEncoding = new UTF8Encoding(false); | |
| public const string DefaultRootElementName = "root"; | |
| private readonly XmlWriter _output; | |
| // The default element name used for WriteMessageStart | |
| private string _rootElementName; | |
| // Used to assert matching WriteMessageStart/WriteMessageEnd calls | |
| private int _messageOpenCount; | |
| private static XmlWriterSettings DefaultSettings(Encoding encoding) | |
| { | |
| return new XmlWriterSettings() | |
| { | |
| CheckCharacters = false, | |
| NewLineHandling = NewLineHandling.Entitize, | |
| OmitXmlDeclaration = true, | |
| Encoding = encoding, | |
| }; | |
| } | |
| /// <summary> | |
| /// Constructs the XmlFormatWriter to write to the given TextWriter | |
| /// </summary> | |
| public static XmlFormatWriter CreateInstance(TextWriter output) | |
| { | |
| return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(output.Encoding))); | |
| } | |
| /// <summary> | |
| /// Constructs the XmlFormatWriter to write to the given stream | |
| /// </summary> | |
| public static XmlFormatWriter CreateInstance(Stream output) | |
| { | |
| return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(DefaultEncoding))); | |
| } | |
| /// <summary> | |
| /// Constructs the XmlFormatWriter to write to the given stream | |
| /// </summary> | |
| public static XmlFormatWriter CreateInstance(Stream output, Encoding encoding) | |
| { | |
| return new XmlFormatWriter(XmlWriter.Create(output, DefaultSettings(encoding))); | |
| } | |
| /// <summary> | |
| /// Constructs the XmlFormatWriter to write to the given XmlWriter | |
| /// </summary> | |
| public static XmlFormatWriter CreateInstance(XmlWriter output) | |
| { | |
| return new XmlFormatWriter(output); | |
| } | |
| protected XmlFormatWriter(XmlWriter output) | |
| { | |
| _output = output; | |
| _messageOpenCount = 0; | |
| _rootElementName = DefaultRootElementName; | |
| } | |
| /// <summary> | |
| /// Gets or sets the default element name to use when using the Merge<TBuilder>() | |
| /// </summary> | |
| public string RootElementName | |
| { | |
| get { return _rootElementName; } | |
| set | |
| { | |
| ThrowHelper.ThrowIfNull(value, "RootElementName"); | |
| _rootElementName = value; | |
| } | |
| } | |
| /// <summary> | |
| /// Gets or sets the options to use while generating the XML | |
| /// </summary> | |
| public XmlWriterOptions Options { get; set; } | |
| /// <summary> | |
| /// Sets the options to use while generating the XML | |
| /// </summary> | |
| public XmlFormatWriter SetOptions(XmlWriterOptions options) | |
| { | |
| Options = options; | |
| return this; | |
| } | |
| private bool TestOption(XmlWriterOptions option) | |
| { | |
| return (Options & option) != 0; | |
| } | |
| /// <summary> | |
| /// Completes any pending write operations | |
| /// </summary> | |
| public override void Flush() | |
| { | |
| _output.Flush(); | |
| base.Flush(); | |
| } | |
| /// <summary> | |
| /// Used to write the root-message preamble, in xml this is open element for RootElementName, | |
| /// by default "<root>". After this call you can call IMessageLite.MergeTo(...) and | |
| /// complete the message with a call to WriteMessageEnd(). | |
| /// </summary> | |
| public override void WriteMessageStart() | |
| { | |
| WriteMessageStart(_rootElementName); | |
| } | |
| /// <summary> | |
| /// Used to write the root-message preamble, in xml this is open element for elementName. | |
| /// After this call you can call IMessageLite.MergeTo(...) and complete the message with | |
| /// a call to WriteMessageEnd(). | |
| /// </summary> | |
| public void WriteMessageStart(string elementName) | |
| { | |
| if (TestOption(XmlWriterOptions.OutputJsonTypes)) | |
| { | |
| _output.WriteStartElement("root"); // json requires this is the root-element | |
| _output.WriteAttributeString("type", "object"); | |
| } | |
| else | |
| { | |
| _output.WriteStartElement(elementName); | |
| } | |
| _messageOpenCount++; | |
| } | |
| /// <summary> | |
| /// Used to complete a root-message previously started with a call to WriteMessageStart() | |
| /// </summary> | |
| public override void WriteMessageEnd() | |
| { | |
| if (_messageOpenCount <= 0) | |
| { | |
| throw new InvalidOperationException(); | |
| } | |
| _output.WriteEndElement(); | |
| _output.Flush(); | |
| _messageOpenCount--; | |
| } | |
| /// <summary> | |
| /// Writes a message as an element using the name defined in <see cref="RootElementName"/> | |
| /// </summary> | |
| public override void WriteMessage(IMessageLite message) | |
| { | |
| WriteMessage(_rootElementName, message); | |
| } | |
| /// <summary> | |
| /// Writes a message as an element with the given name | |
| /// </summary> | |
| public void WriteMessage(string elementName, IMessageLite message) | |
| { | |
| WriteMessageStart(elementName); | |
| message.WriteTo(this); | |
| WriteMessageEnd(); | |
| } | |
| /// <summary> | |
| /// Writes a message | |
| /// </summary> | |
| protected override void WriteMessageOrGroup(string field, IMessageLite message) | |
| { | |
| _output.WriteStartElement(field); | |
| if (TestOption(XmlWriterOptions.OutputJsonTypes)) | |
| { | |
| _output.WriteAttributeString("type", "object"); | |
| } | |
| message.WriteTo(this); | |
| _output.WriteEndElement(); | |
| } | |
| /// <summary> | |
| /// Writes a String value | |
| /// </summary> | |
| protected override void WriteAsText(string field, string textValue, object typedValue) | |
| { | |
| _output.WriteStartElement(field); | |
| if (TestOption(XmlWriterOptions.OutputJsonTypes)) | |
| { | |
| if (typedValue is int || typedValue is uint || typedValue is long || typedValue is ulong || | |
| typedValue is double || typedValue is float) | |
| { | |
| _output.WriteAttributeString("type", "number"); | |
| } | |
| else if (typedValue is bool) | |
| { | |
| _output.WriteAttributeString("type", "boolean"); | |
| } | |
| } | |
| _output.WriteString(textValue); | |
| //Empty strings should not be written as empty elements '<item/>', rather as '<item></item>' | |
| if (_output.WriteState == WriteState.Element) | |
| { | |
| _output.WriteRaw(""); | |
| } | |
| _output.WriteEndElement(); | |
| } | |
| /// <summary> | |
| /// Writes an array of field values | |
| /// </summary> | |
| protected override void WriteArray(FieldType fieldType, string field, IEnumerable items) | |
| { | |
| //see if it's empty | |
| IEnumerator eitems = items.GetEnumerator(); | |
| try | |
| { | |
| if (!eitems.MoveNext()) | |
| { | |
| return; | |
| } | |
| } | |
| finally | |
| { | |
| if (eitems is IDisposable) | |
| { | |
| ((IDisposable) eitems).Dispose(); | |
| } | |
| } | |
| if (TestOption(XmlWriterOptions.OutputNestedArrays | XmlWriterOptions.OutputJsonTypes)) | |
| { | |
| _output.WriteStartElement(field); | |
| if (TestOption(XmlWriterOptions.OutputJsonTypes)) | |
| { | |
| _output.WriteAttributeString("type", "array"); | |
| } | |
| base.WriteArray(fieldType, "item", items); | |
| _output.WriteEndElement(); | |
| } | |
| else | |
| { | |
| base.WriteArray(fieldType, field, items); | |
| } | |
| } | |
| /// <summary> | |
| /// Writes a System.Enum by the numeric and textual value | |
| /// </summary> | |
| protected override void WriteEnum(string field, int number, string name) | |
| { | |
| _output.WriteStartElement(field); | |
| if (!TestOption(XmlWriterOptions.OutputJsonTypes) && TestOption(XmlWriterOptions.OutputEnumValues)) | |
| { | |
| _output.WriteAttributeString("value", XmlConvert.ToString(number)); | |
| } | |
| _output.WriteString(name); | |
| _output.WriteEndElement(); | |
| } | |
| } | |
| } |