/// <summary> /// Lexes and parses a JSON object as a hash table in the specified text reader. /// </summary> /// <typeparam name="TValue">The type of the values to deserialize.</typeparam> /// <param name="reader">The text reader to lex and parse an object from.</param> /// <param name="context">The parser context.</param> /// <param name="parseValue">The parser to use for values.</param> /// <returns>A hash table, if found; otherwise, a ParseException is thrown.</returns> /// <remarks> /// This method does not consume whitespace; the character at the specified start index in the string is expected to be the start of a valid JSON object. /// </remarks> internal static Dictionary <string, TValue> ParseAnyObject <TValue>(System.IO.TextReader reader, ParserContext context, ParseReaderFunc <TValue> parseValue) { Debug.Assert(reader.Peek() == '{'); // // CONSIDER: Add support for different representations of objects, e.g. ExpandoObject from the DLR. For // now, we'll go with the most obvious choice and users can wrap the resulting value in case // they want to support dynamic behavior. // var res = new Dictionary <string, TValue>(); reader.Read(); SkipWhiteSpace(reader); // NB: We *have* to skip trailing whitespace after the { token. var c = reader.Peek(); if (c < 0) { throw new ParseException("Unexpected end of stream when parsing object expecting an object body or '}' terminator.", -1, ParseError.PrematureEndOfInput); } if (c == '}') { reader.Read(); SkipWhiteSpace(reader); // NB: We *have* to skip trailing whitespace after the } token. return(res); } while (c >= 0) { var key = ParseStringNonNull(reader); SkipWhiteSpace(reader); // NB: This skips leading whitespace before the : token; we can't rely on the trie to do it. c = reader.Peek(); if (c < 0) { throw new ParseException("Unexpected end of stream when parsing object expecting a ':' separator between a key name and a value.", -1, ParseError.PrematureEndOfInput); } if (c != ':') { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Unexpected character '{0}' encountered when parsing object expecting a ':' token to separate key name and a value.", (char)c), -1, ParseError.UnexpectedToken); } reader.Read(); SkipWhiteSpace(reader); // NB: We *have* to skip trailing whitespace after the : token. // // NB: We're not using Add here in order to allow for duplicate keys. The last one in lexical order wins, which is // consistent with behavior of other popular JSON frameworks and the behavior of our strongly typed object // deserializer. // res[key] = parseValue(reader, context); // // CONSIDER: Parsers for primitives don't trim trailing whitespace. We could change this and eliminate the need to // skip whitespace here ourselves. This trimming is duplicate work if we're dealing with objects or // arrays which already guarantee trimming trailing whitespace. // SkipWhiteSpace(reader); c = reader.Peek(); if (c < 0) { throw new ParseException("Unexpected end of stream when parsing object.", -1, ParseError.PrematureEndOfInput); } switch (c) { case ',': reader.Read(); SkipWhiteSpace(reader); // NB: We *have* to skip trailing whitespace after the , token. break; case '}': reader.Read(); SkipWhiteSpace(reader); // NB: We *have* to skip trailing whitespace after the } token. return(res); default: throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Unexpected character '{0}' encountered when parsing object expecting either ',' or '}}'.", (char)c), -1, ParseError.UnexpectedToken); } c = reader.Peek(); } throw new ParseException("Unexpected end of stream when parsing object and expecting a key.", -1, ParseError.PrematureEndOfInput); }
/// <summary> /// Creates a new deserializer given the specified parser implementation. /// </summary> /// <param name="parseString">The parser to use to deserialize JSON payloads from string inputs.</param> /// <param name="parseReader">The parser to use to deserialize JSON payloads from text reader inputs.</param> public Deserializer(ParseStringFunc <T> parseString, ParseReaderFunc <T> parseReader) : base(parseString, parseReader) { _context = new ParserContext(); }
/// <summary> /// Creates a new deserializer given the specified parser implementations. /// </summary> /// <param name="parseString">The parser to use to deserialize JSON payloads from string inputs.</param> /// <param name="parseReader">The parser to use to deserialize JSON payloads from text reader inputs.</param> public DeserializerBase(ParseStringFunc <T> parseString, ParseReaderFunc <T> parseReader) { _parseString = parseString; _parseReader = parseReader; }
/// <summary> /// Creates a new deserializer given the specified parser implementations. /// </summary> /// <param name="parseString">The parser to use to deserialize JSON payloads from string inputs.</param> /// <param name="parseReader">The parser to use to deserialize JSON payloads from text reader inputs.</param> public SafeDeserializer(ParseStringFunc <T> parseString, ParseReaderFunc <T> parseReader) : base(parseString, parseReader) { _contextPool = new ObjectPool <ParserContext>(() => new ParserContext()); }