JSONXmlReader.cs
343 lines
| 12.0 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r163 | using Implab; | ||
using System; | ||||
using System.Collections.Generic; | ||||
using System.Globalization; | ||||
using System.IO; | ||||
using System.Xml; | ||||
cin
|
r180 | namespace Implab.Formats.JSON { | ||
cin
|
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; | ||||
cin
|
r180 | int m_depthCorrection; | ||
cin
|
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; | ||||
cin
|
r180 | |||
return String.Empty; | ||||
cin
|
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() { | ||||
cin
|
r180 | if (m_state != ReadState.Interactive && m_state != ReadState.Initial) | ||
cin
|
r163 | return false; | ||
if (m_state == ReadState.Initial) | ||||
cin
|
r180 | m_state = ReadState.Interactive; | ||
cin
|
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; | ||||
} | ||||
cin
|
r180 | SetLocalName(itemName, true); | ||
cin
|
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; | ||||
} | ||||
cin
|
r180 | m_state = ReadState.EndOfFile; | ||
cin
|
r163 | return false; | ||
} catch { | ||||
cin
|
r180 | m_state = ReadState.Error; | ||
cin
|
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; | ||||
cin
|
r225 | |||
switch(Convert.GetTypeCode (m_parser.ElementValue)) { | ||||
case TypeCode.Double: | ||||
return ((double)m_parser.ElementValue).ToString (CultureInfo.InvariantCulture); | ||||
case TypeCode.String: | ||||
return (string)m_parser.ElementValue; | ||||
case TypeCode.Boolean: | ||||
return (bool)m_parser.ElementValue ? "true" : "false"; | ||||
default: | ||||
return m_parser.ElementValue.ToString (); | ||||
} | ||||
cin
|
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) { | ||||
cin
|
r180 | return new JSONXmlReader(new JSONParser(reader), options); | ||
cin
|
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); | ||||
} | ||||
} | ||||
} | ||||