JSONXmlReader.cs
335 lines
| 11.7 KiB
| text/x-csharp
|
CSharpLexer
|
|
r163 | using Implab; | ||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.IO; | ||||
| using System.Xml; | ||||
|
|
r180 | namespace Implab.Formats.JSON { | ||
|
|
r163 | public class JSONXmlReader : XmlReader { | ||
| enum ValueContext { | ||||
| Undefined, | ||||
| ElementStart, | ||||
| ElementValue, | ||||
| ElementEnd, | ||||
| ElementEmpty | ||||
| } | ||||
| struct LocalNameContext { | ||||
| public string localName; | ||||
| public bool isArray; | ||||
| } | ||||
| JSONParser m_parser; | ||||
| ValueContext m_valueContext; | ||||
| ReadState m_state = ReadState.Initial; | ||||
| Stack<LocalNameContext> m_localNameStack = new Stack<LocalNameContext>(); | ||||
| LocalNameContext m_localName; | ||||
|
|
r180 | int m_depthCorrection; | ||
|
|
r163 | |||
| readonly string m_rootName; | ||||
| readonly string m_prefix; | ||||
| readonly string m_namespaceUri; | ||||
| readonly bool m_flattenArrays; | ||||
| readonly string m_arrayItemName; | ||||
| readonly XmlNameTable m_nameTable; | ||||
| JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { | ||||
| m_parser = parser; | ||||
| if (options != null) { | ||||
| m_prefix = options.NodesPrefix ?? String.Empty; | ||||
| m_namespaceUri = options.NamespaceURI ?? String.Empty; | ||||
| m_rootName = options.RootName ?? "json"; | ||||
| m_flattenArrays = options.FlattenArrays; | ||||
| m_arrayItemName = options.ArrayItemName ?? "item"; | ||||
| m_nameTable = options.NameTable ?? new NameTable(); | ||||
| } else { | ||||
| m_prefix = String.Empty; | ||||
| m_namespaceUri = String.Empty; | ||||
| m_rootName = "json"; | ||||
| m_flattenArrays = false; | ||||
| m_arrayItemName = "item"; | ||||
| m_nameTable = new NameTable(); | ||||
| } | ||||
| } | ||||
| /// <summary> | ||||
| /// Always 0, JSON doesn't support attributes | ||||
| /// </summary> | ||||
| public override int AttributeCount { | ||||
| get { return 0; } | ||||
| } | ||||
| public override string BaseURI { | ||||
| get { return String.Empty; } | ||||
| } | ||||
| public override int Depth { | ||||
| get { | ||||
| return m_localNameStack.Count + m_depthCorrection; | ||||
| } | ||||
| } | ||||
| public override bool EOF { | ||||
| get { return m_parser.EOF; } | ||||
| } | ||||
| /// <summary> | ||||
| /// Always throws an exception | ||||
| /// </summary> | ||||
| /// <param name="i"></param> | ||||
| /// <returns></returns> | ||||
| public override string GetAttribute(int i) { | ||||
| throw new ArgumentOutOfRangeException(); | ||||
| } | ||||
| /// <summary> | ||||
| /// Always returns empty string | ||||
| /// </summary> | ||||
| /// <param name="name"></param> | ||||
| /// <param name="namespaceURI"></param> | ||||
| /// <returns></returns> | ||||
| public override string GetAttribute(string name, string namespaceURI) { | ||||
| return String.Empty; | ||||
| } | ||||
| /// <summary> | ||||
| /// Always returns empty string | ||||
| /// </summary> | ||||
| /// <param name="name"></param> | ||||
| /// <returns></returns> | ||||
| public override string GetAttribute(string name) { | ||||
| return String.Empty; | ||||
| } | ||||
| public override bool IsEmptyElement { | ||||
| get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; } | ||||
| } | ||||
| public override string LocalName { | ||||
| get { return m_localName.localName; } | ||||
| } | ||||
| public override string LookupNamespace(string prefix) { | ||||
| if (String.IsNullOrEmpty(prefix) || prefix == m_prefix) | ||||
| return m_namespaceUri; | ||||
|
|
r180 | |||
| return String.Empty; | ||||
|
|
r163 | } | ||
| public override bool MoveToAttribute(string name, string ns) { | ||||
| return false; | ||||
| } | ||||
| public override bool MoveToAttribute(string name) { | ||||
| return false; | ||||
| } | ||||
| public override bool MoveToElement() { | ||||
| return false; | ||||
| } | ||||
| public override bool MoveToFirstAttribute() { | ||||
| return false; | ||||
| } | ||||
| public override bool MoveToNextAttribute() { | ||||
| return false; | ||||
| } | ||||
| public override XmlNameTable NameTable { | ||||
| get { return m_nameTable; } | ||||
| } | ||||
| public override string NamespaceURI { | ||||
| get { return m_namespaceUri; } | ||||
| } | ||||
| public override XmlNodeType NodeType { | ||||
| get { | ||||
| switch (m_parser.ElementType) { | ||||
| case JSONElementType.BeginObject: | ||||
| case JSONElementType.BeginArray: | ||||
| return XmlNodeType.Element; | ||||
| case JSONElementType.EndObject: | ||||
| case JSONElementType.EndArray: | ||||
| return XmlNodeType.EndElement; | ||||
| case JSONElementType.Value: | ||||
| switch (m_valueContext) { | ||||
| case ValueContext.ElementStart: | ||||
| case ValueContext.ElementEmpty: | ||||
| return XmlNodeType.Element; | ||||
| case ValueContext.ElementValue: | ||||
| return XmlNodeType.Text; | ||||
| case ValueContext.ElementEnd: | ||||
| return XmlNodeType.EndElement; | ||||
| default: | ||||
| throw new InvalidOperationException(); | ||||
| } | ||||
| default: | ||||
| throw new InvalidOperationException(); | ||||
| } | ||||
| } | ||||
| } | ||||
| public override string Prefix { | ||||
| get { return m_prefix; } | ||||
| } | ||||
| public override bool Read() { | ||||
|
|
r180 | if (m_state != ReadState.Interactive && m_state != ReadState.Initial) | ||
|
|
r163 | return false; | ||
| if (m_state == ReadState.Initial) | ||||
|
|
r180 | m_state = ReadState.Interactive; | ||
|
|
r163 | |||
| try { | ||||
| switch (m_parser.ElementType) { | ||||
| case JSONElementType.Value: | ||||
| switch (m_valueContext) { | ||||
| case ValueContext.ElementStart: | ||||
| SetLocalName(String.Empty); | ||||
| m_valueContext = ValueContext.ElementValue; | ||||
| return true; | ||||
| case ValueContext.ElementValue: | ||||
| RestoreLocalName(); | ||||
| m_valueContext = ValueContext.ElementEnd; | ||||
| return true; | ||||
| case ValueContext.ElementEmpty: | ||||
| case ValueContext.ElementEnd: | ||||
| RestoreLocalName(); | ||||
| break; | ||||
| } | ||||
| break; | ||||
| case JSONElementType.EndArray: | ||||
| case JSONElementType.EndObject: | ||||
| RestoreLocalName(); | ||||
| break; | ||||
| } | ||||
| string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; | ||||
| while (m_parser.Read()) { | ||||
| if (!String.IsNullOrEmpty(m_parser.ElementName)) | ||||
| itemName = m_parser.ElementName; | ||||
| switch (m_parser.ElementType) { | ||||
| case JSONElementType.BeginArray: | ||||
| if (m_flattenArrays && !m_localName.isArray) { | ||||
| m_depthCorrection--; | ||||
| SetLocalName(itemName, true); | ||||
| continue; | ||||
| } | ||||
|
|
r180 | SetLocalName(itemName, true); | ||
|
|
r163 | break; | ||
| case JSONElementType.BeginObject: | ||||
| SetLocalName(itemName); | ||||
| break; | ||||
| case JSONElementType.EndArray: | ||||
| if (m_flattenArrays && !m_localNameStack.Peek().isArray) { | ||||
| RestoreLocalName(); | ||||
| m_depthCorrection++; | ||||
| continue; | ||||
| } | ||||
| break; | ||||
| case JSONElementType.EndObject: | ||||
| break; | ||||
| case JSONElementType.Value: | ||||
| SetLocalName(itemName); | ||||
| m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart; | ||||
| break; | ||||
| } | ||||
| return true; | ||||
| } | ||||
|
|
r180 | m_state = ReadState.EndOfFile; | ||
|
|
r163 | return false; | ||
| } catch { | ||||
|
|
r180 | m_state = ReadState.Error; | ||
|
|
r163 | throw; | ||
| } | ||||
| } | ||||
| public override bool ReadAttributeValue() { | ||||
| return false; | ||||
| } | ||||
| public override ReadState ReadState { | ||||
| get { return m_state; } | ||||
| } | ||||
| public override void ResolveEntity() { | ||||
| // do nothing | ||||
| } | ||||
| public override string Value { | ||||
| get { | ||||
| if (m_parser.ElementValue == null) | ||||
| return String.Empty; | ||||
| if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double) | ||||
| return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture); | ||||
|
|
r180 | return m_parser.ElementValue.ToString(); | ||
|
|
r163 | } | ||
| } | ||||
| void SetLocalName(string name) { | ||||
| m_localNameStack.Push(m_localName); | ||||
| m_localName.localName = name; | ||||
| m_localName.isArray = false; | ||||
| } | ||||
| void SetLocalName(string name, bool isArray) { | ||||
| m_localNameStack.Push(m_localName); | ||||
| m_localName.localName = name; | ||||
| m_localName.isArray = isArray; | ||||
| } | ||||
| void RestoreLocalName() { | ||||
| m_localName = m_localNameStack.Pop(); | ||||
| } | ||||
| public override void Close() { | ||||
| } | ||||
| protected override void Dispose(bool disposing) { | ||||
| #if MONO | ||||
| disposing = true; | ||||
| #endif | ||||
| if (disposing) { | ||||
| m_parser.Dispose(); | ||||
| } | ||||
| base.Dispose(disposing); | ||||
| } | ||||
| public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { | ||||
| return Create(File.OpenText(file), options); | ||||
| } | ||||
| /// <summary> | ||||
| /// Creates the XmlReader for the specified text stream with JSON data. | ||||
| /// </summary> | ||||
| /// <param name="reader">Text reader.</param> | ||||
| /// <param name="options">Options.</param> | ||||
| /// <remarks> | ||||
| /// The reader will be disposed when the XmlReader is disposed. | ||||
| /// </remarks> | ||||
| public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { | ||||
|
|
r180 | return new JSONXmlReader(new JSONParser(reader), options); | ||
|
|
r163 | } | ||
| /// <summary> | ||||
| /// Creates the XmlReader for the specified stream with JSON data. | ||||
| /// </summary> | ||||
| /// <param name="stream">Stream.</param> | ||||
| /// <param name="options">Options.</param> | ||||
| /// <remarks> | ||||
| /// The stream will be disposed when the XmlReader is disposed. | ||||
| /// </remarks> | ||||
| public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) { | ||||
| Safe.ArgumentNotNull(stream, "stream"); | ||||
| // HACK don't dispose StreaReader to keep stream opened | ||||
| return Create(new StreamReader(stream), options); | ||||
| } | ||||
| } | ||||
| } | ||||
