/// <summary> /// Lexes and parses a JSON object as a hash table in the specified string starting from the specified index. /// </summary> /// <typeparam name="TValue">The type of the values to deserialize.</typeparam> /// <param name="str">The string to lex and parse an object from.</param> /// <param name="len">The length of the string.</param> /// <param name="i">The index in the string to start lexing from. This value gets updated to the first index position after the JSON value, if found.</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>(string str, int len, ref int i, ParserContext context, ParseStringFunc <TValue> parseValue) { Debug.Assert(str[i] == '{'); // // 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>(); i++; SkipWhiteSpace(str, len, ref i); // NB: We *have* to skip trailing whitespace after the { token. if (i == len) { throw new ParseException("Unexpected end of stream when parsing object expecting an object body or '}' terminator.", i, ParseError.PrematureEndOfInput); } if (str[i] == '}') { i++; SkipWhiteSpace(str, len, ref i); // NB: We *have* to skip trailing whitespace after the } token. return(res); } while (i < len) { var key = ParseStringNonNull(str, len, ref i); SkipWhiteSpace(str, len, ref i); // NB: This skips leading whitespace before the : token; we can't rely on the trie to do it. if (i == len) { throw new ParseException("Unexpected end of stream when parsing object expecting a ':' separator between a key name and a value.", i, ParseError.PrematureEndOfInput); } if (str[i] != ':') { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Unexpected character '{0}' encountered when parsing object expecting a ':' token to separate key name and a value.", str[i]), i, ParseError.UnexpectedToken); } i++; SkipWhiteSpace(str, len, ref i); // 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(str, len, ref i, 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(str, len, ref i); if (i == len) { throw new ParseException("Unexpected end of stream when parsing object.", i, ParseError.PrematureEndOfInput); } switch (str[i]) { case ',': i++; SkipWhiteSpace(str, len, ref i); // NB: We *have* to skip trailing whitespace after the , token. break; case '}': i++; SkipWhiteSpace(str, len, ref i); // 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 '}}'.", str[i]), i, ParseError.UnexpectedToken); } } Debug.Assert(i == len); throw new ParseException("Unexpected end of stream when parsing object and expecting a key.", i, 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> public DeserializerBase(ParseStringFunc <T> parseString) { _parseString = parseString; }
/// <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 implementation. /// </summary> /// <param name="parseString">The parser to use to deserialize JSON payloads from string inputs.</param> public Deserializer(ParseStringFunc <T> parseString) : base(parseString) { _context = new ParserContext(); }
/// <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 implementation. /// </summary> /// <param name="parseString">The parser to use to deserialize JSON payloads from string inputs.</param> public SafeDeserializer(ParseStringFunc <T> parseString) : base(parseString) { _contextPool = new ObjectPool <ParserContext>(() => new ParserContext()); }