123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Xml;
- namespace Google.ProtocolBuffers.Serialization
- {
- /// <summary>
- /// Parses a proto buffer from an XML document or fragment. .NET 3.5 users may also
- /// use this class to process Json by setting the options to support Json and providing
- /// an XmlReader obtained from <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory"/>.
- /// </summary>
- public class XmlFormatReader : AbstractTextReader
- {
- public const string DefaultRootElementName = XmlFormatWriter.DefaultRootElementName;
- private readonly XmlReader _input;
- private string _rootElementName;
- private static XmlReaderSettings DefaultSettings
- {
- get
- {
- return new XmlReaderSettings()
- {CheckCharacters = false, IgnoreComments = true, IgnoreProcessingInstructions = true};
- }
- }
- /// <summary>
- /// Constructs the XmlFormatReader using the stream provided as the xml
- /// </summary>
- public static XmlFormatReader CreateInstance(byte[] input)
- {
- return new XmlFormatReader(XmlReader.Create(new MemoryStream(input, false), DefaultSettings));
- }
- /// <summary>
- /// Constructs the XmlFormatReader using the stream provided as the xml
- /// </summary>
- public static XmlFormatReader CreateInstance(Stream input)
- {
- return new XmlFormatReader(XmlReader.Create(input, DefaultSettings));
- }
- /// <summary>
- /// Constructs the XmlFormatReader using the string provided as the xml to be read
- /// </summary>
- public static XmlFormatReader CreateInstance(String input)
- {
- return new XmlFormatReader(XmlReader.Create(new StringReader(input), DefaultSettings));
- }
- /// <summary>
- /// Constructs the XmlFormatReader using the xml in the TextReader
- /// </summary>
- public static XmlFormatReader CreateInstance(TextReader input)
- {
- return new XmlFormatReader(XmlReader.Create(input, DefaultSettings));
- }
- /// <summary>
- /// Constructs the XmlFormatReader with the XmlReader
- /// </summary>
- public static XmlFormatReader CreateInstance(XmlReader input)
- {
- return new XmlFormatReader(input);
- }
- /// <summary>
- /// Constructs the XmlFormatReader with the XmlReader and options
- /// </summary>
- protected XmlFormatReader(XmlReader input)
- {
- _input = input;
- _rootElementName = DefaultRootElementName;
- Options = XmlReaderOptions.None;
- }
- /// <summary>
- /// Constructs the XmlFormatReader with the XmlReader and options
- /// </summary>
- protected XmlFormatReader(XmlFormatReader copyFrom, XmlReader input)
- : base(copyFrom)
- {
- _input = input;
- _rootElementName = copyFrom._rootElementName;
- Options = copyFrom.Options;
- }
- /// <summary>
- /// Gets or sets the options to use when reading the xml
- /// </summary>
- public XmlReaderOptions Options { get; set; }
- /// <summary>
- /// Sets the options to use while generating the XML
- /// </summary>
- public XmlFormatReader SetOptions(XmlReaderOptions options)
- {
- Options = options;
- return this;
- }
- /// <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;
- }
- }
- private XmlFormatReader CloneWith(XmlReader rdr)
- {
- XmlFormatReader copy = new XmlFormatReader(this, rdr);
- return copy;
- }
- private void NextElement()
- {
- while (!_input.IsStartElement() && _input.Read())
- {
- continue;
- }
- }
- private static void Assert(bool cond)
- {
- if (!cond)
- {
- throw new FormatException();
- }
- }
- /// <summary>
- /// Reads the root-message preamble specific to this formatter
- /// </summary>
- public override AbstractReader ReadStartMessage()
- {
- return ReadStartMessage(_rootElementName);
- }
- public AbstractReader ReadStartMessage(string element)
- {
- string field;
- Assert(PeekNext(out field) && field == element);
- XmlReader child = _input.ReadSubtree();
- while (!child.IsStartElement() && child.Read())
- {
- continue;
- }
- child.Read();
- return CloneWith(child);
- }
- /// <summary>
- /// Reads the root-message close specific to this formatter, MUST be called
- /// on the reader obtained from ReadStartMessage(string element).
- /// </summary>
- public override void ReadEndMessage()
- {
- Assert(0 == _input.Depth);
- if(_input.NodeType == XmlNodeType.EndElement)
- {
- _input.Read();
- }
- }
- /// <summary>
- /// Merge the provided builder as an element named <see cref="RootElementName"/> in the current context
- /// </summary>
- public override TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
- {
- return Merge(_rootElementName, builder, registry);
- }
- /// <summary>
- /// Merge the provided builder as an element of the current context
- /// </summary>
- public TBuilder Merge<TBuilder>(string element, TBuilder builder) where TBuilder : IBuilderLite
- {
- return Merge(element, builder, ExtensionRegistry.Empty);
- }
- /// <summary>
- /// Merge the provided builder as an element of the current context
- /// </summary>
- public TBuilder Merge<TBuilder>(string element, TBuilder builder, ExtensionRegistry registry)
- where TBuilder : IBuilderLite
- {
- string field;
- Assert(PeekNext(out field) && field == element);
- ReadMessage(builder, registry);
- return builder;
- }
- /// <summary>
- /// Peeks at the next field in the input stream and returns what information is available.
- /// </summary>
- /// <remarks>
- /// This may be called multiple times without actually reading the field. Only after the field
- /// is either read, or skipped, should PeekNext return a different value.
- /// </remarks>
- protected override bool PeekNext(out string field)
- {
- NextElement();
- if (_input.IsStartElement())
- {
- field = _input.LocalName;
- return true;
- }
- field = null;
- return false;
- }
- /// <summary>
- /// Causes the reader to skip past this field
- /// </summary>
- protected override void Skip()
- {
- if (_input.IsStartElement())
- {
- if (!_input.IsEmptyElement)
- {
- int depth = _input.Depth;
- while (_input.Depth >= depth && _input.NodeType != XmlNodeType.EndElement)
- {
- Assert(_input.Read());
- }
- }
- _input.Read();
- }
- }
- /// <summary>
- /// returns true if it was able to read a single value into the value reference. The value
- /// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
- /// </summary>
- protected override bool ReadEnum(ref object value)
- {
- int number;
- string temp;
- if (null != (temp = _input.GetAttribute("value")) && int.TryParse(temp, out number))
- {
- Skip();
- value = number;
- return true;
- }
- return base.ReadEnum(ref value);
- }
- /// <summary>
- /// Returns true if it was able to read a String from the input
- /// </summary>
- protected override bool ReadAsText(ref string value, Type type)
- {
- Assert(_input.NodeType == XmlNodeType.Element);
- value = _input.ReadElementContentAsString();
- return true;
- }
- /// <summary>
- /// Merges the input stream into the provided IBuilderLite
- /// </summary>
- protected override bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry)
- {
- Assert(_input.IsStartElement());
- if (!_input.IsEmptyElement)
- {
- int depth = _input.Depth;
- XmlReader child = _input.ReadSubtree();
- while (!child.IsStartElement() && child.Read())
- {
- continue;
- }
- child.Read();
- builder.WeakMergeFrom(CloneWith(child), registry);
- Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
- }
- _input.Read();
- return true;
- }
- private IEnumerable<string> NonNestedArrayItems(string field)
- {
- return base.ForeachArrayItem(field);
- }
- /// <summary>
- /// Cursors through the array elements and stops at the end of the array
- /// </summary>
- protected override IEnumerable<string> ForeachArrayItem(string field)
- {
- bool isNested = (Options & XmlReaderOptions.ReadNestedArrays) != 0;
- if (!isNested)
- {
- foreach (string item in NonNestedArrayItems(field))
- {
- yield return item;
- }
- yield break;
- }
- if (!_input.IsEmptyElement)
- {
- int depth = _input.Depth;
- XmlReader child = _input.ReadSubtree();
- while (!child.IsStartElement() && child.Read())
- {
- continue;
- }
- child.Read();
- foreach (string item in CloneWith(child).NonNestedArrayItems("item"))
- {
- yield return item;
- }
- Assert(depth == _input.Depth && _input.NodeType == XmlNodeType.EndElement);
- }
- _input.Read();
- yield break;
- }
- }
- }
|