JSONXmlReader.cs
343 lines
| 12.0 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r58 | using Implab; | ||
using Implab.Parsing; | ||||
using System; | ||||
using System.Collections.Generic; | ||||
cin
|
r63 | using System.Globalization; | ||
cin
|
r60 | using System.IO; | ||
cin
|
r58 | using System.Linq; | ||
using System.Text; | ||||
using System.Threading.Tasks; | ||||
using System.Xml; | ||||
cin
|
r60 | namespace Implab.JSON { | ||
cin
|
r58 | 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; | ||||
int m_depthCorrection = 0; | ||||
cin
|
r60 | readonly string m_rootName; | ||
readonly string m_prefix; | ||||
readonly string m_namespaceUri; | ||||
readonly bool m_flattenArrays; | ||||
readonly string m_arrayItemName; | ||||
readonly XmlNameTable m_nameTable; | ||||
cin
|
r63 | |||
cin
|
r64 | JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { | ||
cin
|
r58 | m_parser = parser; | ||
cin
|
r60 | |||
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(); | ||||
} | ||||
cin
|
r58 | } | ||
/// <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 { | ||||
cin
|
r63 | return m_localNameStack.Count + m_depthCorrection; | ||
cin
|
r58 | } | ||
} | ||||
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; | ||||
else | ||||
return String.Empty; | ||||
} | ||||
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() { | ||||
if (m_state != System.Xml.ReadState.Interactive && m_state != System.Xml.ReadState.Initial) | ||||
return false; | ||||
if (m_state == ReadState.Initial) | ||||
m_state = System.Xml.ReadState.Interactive; | ||||
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; | ||||
} | ||||
cin
|
r60 | string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; | ||
cin
|
r58 | 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; | ||||
} else { | ||||
SetLocalName(itemName, true); | ||||
} | ||||
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; | ||||
default: | ||||
break; | ||||
} | ||||
return true; | ||||
} | ||||
m_state = System.Xml.ReadState.EndOfFile; | ||||
return false; | ||||
} catch { | ||||
m_state = System.Xml.ReadState.Error; | ||||
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 { | ||||
cin
|
r63 | 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); | ||||
else | ||||
cin
|
r78 | return m_parser.ElementValue.ToString(); | ||
cin
|
r63 | } | ||
cin
|
r58 | } | ||
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() { | ||||
cin
|
r63 | |||
cin
|
r58 | } | ||
cin
|
r59 | |||
protected override void Dispose(bool disposing) { | ||||
cin
|
r85 | #if MONO | ||
disposing = true; | ||||
#endif | ||||
cin
|
r59 | if (disposing) { | ||
cin
|
r64 | m_parser.Dispose(); | ||
cin
|
r59 | } | ||
base.Dispose(disposing); | ||||
} | ||||
cin
|
r64 | public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { | ||
return Create(File.OpenText(file), options); | ||||
} | ||||
cin
|
r60 | |||
cin
|
r79 | /// <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> | ||||
cin
|
r64 | public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { | ||
return new JSONXmlReader(new JSONParser(reader, true), options); | ||||
} | ||||
cin
|
r79 | /// <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> | ||||
cin
|
r64 | 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); | ||||
cin
|
r60 | } | ||
cin
|
r58 | } | ||
} | ||||