Click or drag to resize
Json.NET

Custom JsonReader

 

This sample creates a custom JsonReader.

Sample
Types
public class XmlJsonReader : JsonReader
{
    private readonly Stack<JTokenType> _stateStack;
    private readonly XmlReader _reader;

    private JTokenType? _valueType;

    public XmlJsonReader(XmlReader reader)
    {
        _reader = reader;
        _stateStack = new Stack<JTokenType>();
    }

    private JTokenType PeekState()
    {
        JTokenType current = (_stateStack.Count > 0) ? _stateStack.Peek() : JTokenType.None;
        return current;
    }

    public override bool Read()
    {
        if (HandleValueType())
        {
            return true;
        }

        while (_reader.Read())
        {
            switch (_reader.NodeType)
            {
                case XmlNodeType.Element:
                    string typeName = _reader.GetAttribute("type");
                    if (typeName == null)
                    {
                        throw new Exception("No type specified.");
                    }

                    _valueType = (JTokenType)Enum.Parse(typeof(JTokenType), typeName, true);

                    switch (PeekState())
                    {
                        case JTokenType.None:
                            HandleValueType();
                            return true;
                        case JTokenType.Object:
                            SetToken(JsonToken.PropertyName, _reader.LocalName);
                            _stateStack.Push(JTokenType.Property);
                            return true;
                        case JTokenType.Array:
                        case JTokenType.Constructor:
                            continue;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                case XmlNodeType.EndElement:
                    switch (_stateStack.Peek())
                    {
                        case JTokenType.Object:
                            SetToken(JsonToken.EndObject);
                            _stateStack.Pop();
                            if (PeekState() == JTokenType.Property)
                            {
                                _stateStack.Pop();
                            }
                            return true;
                        case JTokenType.Array:
                            SetToken(JsonToken.EndArray);
                            _stateStack.Pop();
                            if (PeekState() == JTokenType.Property)
                            {
                                _stateStack.Pop();
                            }
                            return true;
                        case JTokenType.Constructor:
                            SetToken(JsonToken.EndConstructor);
                            _stateStack.Pop();
                            if (PeekState() == JTokenType.Property)
                            {
                                _stateStack.Pop();
                            }
                            return true;
                    }

                    _stateStack.Pop();
                    if (PeekState() == JTokenType.Property)
                    {
                        _stateStack.Pop();
                    }

                    break;
                case XmlNodeType.Text:
                case XmlNodeType.CDATA:
                    switch (_valueType)
                    {
                        case JTokenType.Integer:
                            SetToken(JsonToken.Integer, Convert.ToInt64(_reader.Value, CultureInfo.InvariantCulture));
                            break;
                        case JTokenType.Float:
                            SetToken(JsonToken.Float, Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture));
                            break;
                        case JTokenType.String:
                        case JTokenType.Uri:
                        case JTokenType.TimeSpan:
                        case JTokenType.Guid:
                            SetToken(JsonToken.String, _reader.Value);
                            break;
                        case JTokenType.Boolean:
                            SetToken(JsonToken.Boolean, Convert.ToBoolean(_reader.Value, CultureInfo.InvariantCulture));
                            break;
                        case JTokenType.Date:
                            SetToken(JsonToken.Date, Convert.ToDateTime(_reader.Value, CultureInfo.InvariantCulture));
                            break;
                        case JTokenType.Bytes:
                            SetToken(JsonToken.Bytes, Convert.FromBase64String(_reader.Value));
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    _stateStack.Push(_valueType.Value);
                    return true;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        return false;
    }

    private bool HandleValueType()
    {
        switch (_valueType)
        {
            case JTokenType.Null:
                SetToken(JsonToken.Null);
                _valueType = null;

                if (PeekState() == JTokenType.Property)
                {
                    _stateStack.Pop();
                }
                return true;
            case JTokenType.Object:
                SetToken(JsonToken.StartObject);
                _stateStack.Push(JTokenType.Object);
                _valueType = null;
                return true;
            case JTokenType.Array:
                SetToken(JsonToken.StartArray);
                _stateStack.Push(JTokenType.Array);
                _valueType = null;
                return true;
            case JTokenType.Constructor:
                string constructorName = _reader.GetAttribute("name");
                if (constructorName == null)
                {
                    throw new Exception("No constructor name specified.");
                }

                SetToken(JsonToken.StartConstructor, constructorName);
                _stateStack.Push(JTokenType.Constructor);
                _valueType = null;
                return true;
        }
        return false;
    }

    public override int? ReadAsInt32()
    {
        if (!Read())
        {
            return null;
        }

        return (Value != null) ? (int?)Convert.ToInt32(Value) : null;
    }

    public override string ReadAsString()
    {
        if (!Read())
        {
            return null;
        }

        return (string)Value;
    }

    public override byte[] ReadAsBytes()
    {
        if (!Read())
        {
            return null;
        }

        return (byte[])Value;
    }

    public override decimal? ReadAsDecimal()
    {
        if (!Read())
        {
            return null;
        }

        return (Value != null) ? (decimal?)Convert.ToDecimal(Value) : null;
    }

    public override DateTime? ReadAsDateTime()
    {
        if (!Read())
        {
            return null;
        }

        return (Value != null) ? (DateTime?)Convert.ToDateTime(Value) : null;
    }

    public override DateTimeOffset? ReadAsDateTimeOffset()
    {
        if (!Read())
        {
            return null;
        }

        return (Value != null) ? (DateTimeOffset?)Convert.ToDateTime(Value) : null;
    }
}
Usage
string xml = @"<Root type=""Object"">
  <Null type=""Null"" />
  <String type=""String"">This is a string!</String>
  <Char type=""String"">!</Char>
  <Integer type=""Integer"">123</Integer>
  <DateTime type=""Date"">2001-02-22T20:59:59Z</DateTime>
  <DateTimeOffset type=""Date"">2001-02-22T20:59:59+12:00</DateTimeOffset>
  <Float type=""Float"">1.1</Float>
  <Double type=""Float"">3.14</Double>
  <Decimal type=""Float"">19.95</Decimal>
  <Guid type=""Guid"">d66eab59-3715-4b35-9e06-fa61c1216eaa</Guid>
  <Uri type=""Uri"">http://james.newtonking.com</Uri>
  <Array type=""Array"">
    <Item type=""Integer"">1</Item>
    <Item type=""Bytes"">SGVsbG8gd29ybGQh</Item>
    <Item type=""Boolean"">True</Item>
  </Array>
  <Object type=""Object"">
    <String type=""String"">This is a string!</String>
    <Null type=""Null"" />
  </Object>
  <Constructor type=""Constructor"" name=""Date"">
    <Item type=""Integer"">2000</Item>
    <Item type=""Integer"">12</Item>
    <Item type=""Integer"">30</Item>
  </Constructor>
</Root>";

StringReader sr = new StringReader(xml);

using (XmlReader xmlReader = XmlReader.Create(sr, new XmlReaderSettings { IgnoreWhitespace = true }))
using (XmlJsonReader reader = new XmlJsonReader(xmlReader))
{
    JObject o = JObject.Load(reader);
    //{
    //  "Null": null,
    //  "String": "This is a string!",
    //  "Char": "!",
    //  "Integer": 123,
    //  "DateTime": "2001-02-23T09:59:59+13:00",
    //  "DateTimeOffset": "2001-02-22T21:59:59+13:00",
    //  "Float": 1.1,
    //  "Double": 3.14,
    //  "Decimal": 19.95,
    //  "Guid": "d66eab59-3715-4b35-9e06-fa61c1216eaa",
    //  "Uri": "http://james.newtonking.com",
    //  "Array": [
    //    1,
    //    "SGVsbG8gd29ybGQh",
    //    true
    //  ],
    //  "Object": {
    //    "String": "This is a string!",
    //    "Null": null
    //  },
    //  "Constructor": new Date(2000, 12, 30)
    //}
}