// JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
//
// Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Globalization;
using System.Collections;
using System.Dynamic;
namespace Topten.JsonKit
{
///
/// Implements the IJsonReader interface
///
public class JsonReader : IJsonReader
{
static JsonReader()
{
// Setup default resolvers
_parserResolver = ResolveParser;
_intoParserResolver = ResolveIntoParser;
Func simpleConverter = (reader, type) =>
{
return reader.ReadLiteral(literal => Convert.ChangeType(literal, type, CultureInfo.InvariantCulture));
};
Func numberConverter = (reader, type) =>
{
return reader.ReadLiteralNumber(type);
};
// Default type handlers
_parsers.Set(typeof(string), simpleConverter);
_parsers.Set(typeof(char), simpleConverter);
_parsers.Set(typeof(bool), simpleConverter);
_parsers.Set(typeof(byte), numberConverter);
_parsers.Set(typeof(sbyte), numberConverter);
_parsers.Set(typeof(short), numberConverter);
_parsers.Set(typeof(ushort), numberConverter);
_parsers.Set(typeof(int), numberConverter);
_parsers.Set(typeof(uint), numberConverter);
_parsers.Set(typeof(long), numberConverter);
_parsers.Set(typeof(ulong), numberConverter);
_parsers.Set(typeof(decimal), numberConverter);
_parsers.Set(typeof(float), numberConverter);
_parsers.Set(typeof(double), numberConverter);
_parsers.Set(typeof(DateTime), (reader, type) =>
{
return reader.ReadLiteral(literal => Utils.FromUnixMilliseconds((long)Convert.ChangeType(literal, typeof(long), CultureInfo.InvariantCulture)));
});
_parsers.Set(typeof(byte[]), (reader, type) =>
{
if (reader.CurrentToken == Token.OpenSquare)
throw new CancelReaderException();
return reader.ReadLiteral(literal => Convert.FromBase64String((string)Convert.ChangeType(literal, typeof(string), CultureInfo.InvariantCulture)));
});
_parsers.Set(typeof(Guid), (reader, type) =>
{
return (Guid)reader.ReadLiteral((val) =>
{
if (val is string str)
return new Guid(str);
throw new InvalidDataException("Expected string guid");
});
});
}
///
/// Constructs a new JsonReader
///
/// The input TextReader stream
/// Options controlling parsing behaviour
public JsonReader(TextReader r, JsonOptions options)
{
_tokenizer = new Tokenizer(r, options);
_options = options;
}
Tokenizer _tokenizer;
JsonOptions _options;
List _contextStack = new List();
///
/// Gets the current parse context (ie: parent key path)
///
public string Context
{
get
{
return string.Join(".", _contextStack);
}
}
static Action ResolveIntoParser(Type type)
{
var ri = ReflectionInfo.GetReflectionInfo(type);
if (ri != null)
return ri.ParseInto;
else
return null;
}
static Func ResolveParser(Type type)
{
// See if the Type has a static parser method - T ParseJson(IJsonReader)
var parseJson = ReflectionInfo.FindParseJson(type);
if (parseJson != null)
{
if (parseJson.GetParameters()[0].ParameterType == typeof(IJsonReader))
{
return (r, t) => parseJson.Invoke(null, new Object[] { r });
}
else
{
return (r, t) =>
{
if (r.GetLiteralKind() == LiteralKind.String)
{
var o = parseJson.Invoke(null, new Object[] { r.GetLiteralString() });
r.NextToken();
return o;
}
throw new InvalidDataException(string.Format("Expected string literal for type {0}", type.FullName));
};
}
}
return (r, t) =>
{
var into = DecoratingActivator.CreateInstance(type);
r.ParseInto(into);
return into;
};
}
///
/// Gets the current position in the input stream
///
public LineOffset CurrentTokenPosition
{
get { return _tokenizer.CurrentTokenPosition; }
}
///
public Token CurrentToken
{
get { return _tokenizer.CurrentToken; }
}
// ReadLiteral is implemented with a converter callback so that any
// errors on converting to the target type are thrown before the tokenizer
// is advanced to the next token. This ensures error location is reported
// at the start of the literal, not the following token.
///
public object ReadLiteral(Func