diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -30,6 +30,7 @@ + @@ -60,6 +61,7 @@ + diff --git a/Implab/JSON/JSONXmlReader.cs b/Implab/JSON/JSONXmlReader.cs new file mode 100644 --- /dev/null +++ b/Implab/JSON/JSONXmlReader.cs @@ -0,0 +1,278 @@ +using Implab; +using Implab.JSON; +using Implab.Parsing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace ConsPlay { + 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 m_localNameStack = new Stack(); + LocalNameContext m_localName; + string m_rootName = "json"; + string m_prefix = String.Empty; + string m_namespaceUri = String.Empty; + bool m_flattenArrays = false; + NameTable m_nameTable = new NameTable(); + int m_depthCorrection = 0; + + public JSONXmlReader(JSONParser parser) { + Safe.ArgumentNotNull(parser, "parser"); + m_parser = parser; + } + + /// + /// Always 0, JSON doesn't support attributes + /// + 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; } + } + + /// + /// Always throws an exception + /// + /// + /// + public override string GetAttribute(int i) { + throw new ArgumentOutOfRangeException(); + } + + /// + /// Always returns empty string + /// + /// + /// + /// + public override string GetAttribute(string name, string namespaceURI) { + return String.Empty; + } + + /// + /// Always returns empty string + /// + /// + /// + 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; + } + string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : "item"; + 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 { + get { return m_parser.ElementValue == null ? String.Empty : m_parser.ElementValue.ToString(); } + } + + 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() { + m_state = System.Xml.ReadState.EndOfFile ; + } + } +}