/// <summary> /// Read the fractional part of the number. /// </summary> /// <param name="path">path to the value being read</param> /// <param name="pbr"></param> /// <param name="builder"></param> /// <exception cref="System.IO.IOException"/> /// <exception cref="Gavaghan.JSON.JSONException"/> private void ReadFractionalPart(string path, PushbackReader pbr, StringBuilder builder) { char c; c = JSONValueFactory.Demand(pbr); if (c == '.') { builder.Append(c); for (; ;) { c = JSONValueFactory.Demand(pbr); if (!Char.IsDigit(c)) { if (builder.ToString().EndsWith(".")) { throw new JSONException(path, "Digits expected after decimal points."); } pbr.Unread(c); break; } builder.Append(c); } } else { pbr.Unread(c); } }
/// <summary> /// Read a JSON value. /// </summary> /// <param name="path">JSON path to the value we're reading</param> /// <param name="pbr">a pushback reader</param> /// <returns>the next JSON value</returns> /// <exception cref="System.IO.IOException" /> /// <exception cref="Gavaghan.JSON.JSONException" /> public IJSONValue Read(string path, PushbackReader pbr) { IJSONValue value; char c = Demand(pbr); // is it a string? if (c == '\"') { value = OnString(path, pbr); } // is it a number? else if (Char.IsDigit(c) || (c == '-')) { value = OnNumber(path, pbr); } // is it an array? else if (c == '[') { value = OnArray(path, pbr); } // is it an object? else if (c == '{') { value = OnObject(path, pbr); } // is it a boolean? else if ((c == 't') || (c == 'f')) { value = OnBoolean(path, pbr); } // is it a null? else if (c == 'n') { value = OnNull(path, pbr); } // else, value type else { value = OnUnknown(path, pbr, c); } // unread trigger character pbr.Unread(c); // implementation specific read value.Read(path, pbr); // give subtype a chance to select a different implementation IJSONValue recast = Recast(path, value); // if value was recast, copy over original data if (recast != null) { recast.CopyValue(value); value = recast; } return(value); }
/// <summary> /// Skip until the first non-whitespace character where comments are /// also treated as whitespace. /// </summary> /// <param name="pbr"></param> /// <exception cref="System.IO.IOException"/> /// <exception cref="Gavaghan.JSON.JSONException"/> public override void SkipWhitespace(PushbackReader pbr) { for (; ;) { int c = pbr.Read(); if (c < 0) { break; // bail on EOF } if (!IsWhitespace(c)) { // it's not whitespace, so see if it's the start of a comment if (c == '/') { int next = pbr.Read(); // is it a line comment? if (next == '/') { SkipLineComment(pbr); continue; } // is it a block comment? else if (next == '*') { SkipBlockComment(pbr); continue; } // else, unread - it's the end of the whitespace pbr.Unread(c); } pbr.Unread(c); break; } } }
/// <summary> /// Read a JSON value (presumes the key has already been read) and set the /// underlying value. There's generally no reason to call this method /// directly. It is intended to be overridden by an extended type. /// </summary> /// <param name="path">path to the value being read</param> /// <param name="pbr">source reader</param> /// <exception cref="Gavaghan.JSON.JSONException">on grammar error</exception> /// <exception cref="System.IO.IOException">on read failure</exception> public override void Read(string path, PushbackReader pbr) { char c = JSONValueFactory.Demand(pbr); if (c != '[') { throw new JSONException(path, "Content does not appear to be an array."); } // empty array is an easy out mFactory.SkipWhitespace(pbr); c = JSONValueFactory.Demand(pbr); if (c == ']') { return; } pbr.Unread(c); // loop through values try { for (; ;) { IJSONValue value = mFactory.Read(path, pbr); mValue.Add(value); // get next non-whitespace mFactory.SkipWhitespace(pbr); c = JSONValueFactory.Demand(pbr); // is end? if (c == ']') { return; } // is more if (c == ',') { mFactory.SkipWhitespace(pbr); continue; } throw new JSONException(path, "Incorrectly formatted array: " + c); } } finally { mFactory = null; } }
/// <summary> /// Read the exponent. /// </summary> /// <param name="path">path to the value being read</param> /// <param name="pbr"></param> /// <param name="builder"></param> /// <exception cref="System.IO.IOException"/> /// <exception cref="Gavaghan.JSON.JSONException"/> private void ReadExponent(string path, PushbackReader pbr, StringBuilder builder) { char c; c = JSONValueFactory.Demand(pbr); if (c == 'e' || (c == 'E')) { builder.Append(c); c = JSONValueFactory.Demand(pbr); if (Char.IsDigit(c) || (c == '+') || (c == '-')) { builder.Append(c); for (; ;) { c = JSONValueFactory.Demand(pbr); if (!Char.IsDigit(c)) { pbr.Unread(c); break; } builder.Append(c); } } else { throw new JSONException(path, "Content does not appear to be a number"); } } else { pbr.Unread(c); } }
/// <summary> /// Skip to first non-whitespace character. /// </summary> /// <param name="pbr">a pushback reader</param> /// <exception cref="System.IO.IOException"/> public virtual void SkipWhitespace(PushbackReader pbr) { for (; ;) { int c = pbr.Read(); if (c < 0) { break; // bail on EOF } // if non-whitespace found, push it back and exit if (!IsWhitespace(c)) { pbr.Unread(c); break; } } }
/// <summary> /// Read the JSON value that comes after the whitespace (if any). /// </summary> /// <param name="reader"></param> /// <returns></returns> /// <exception cref="System.IO.IOException" /> /// <exception cref="Gavaghan.JSON.JSONException" /> public IJSONValue Read(TextReader reader) { PushbackReader pbr = new PushbackReader(reader, PushbackBufferSize); // look for start of value SkipWhitespace(pbr); int c = pbr.Read(); // bail out early if EOF if (c < 0) { return(null); } pbr.Unread(c); return(Read("$", pbr)); }
/// <summary> /// Read the whole portion of a number. /// </summary> /// <param name="pbr"></param> /// <param name="builder"></param> /// <exception cref="System.IO.IOException"/> /// <exception cref="Gavaghan.JSON.JSONException"/> private void ReadWholePart(PushbackReader pbr, StringBuilder builder) { char c; for (; ;) { c = JSONValueFactory.Demand(pbr); if (Char.IsDigit(c)) { builder.Append(c); } else { pbr.Unread(c); break; } } }
/// <summary> /// Read a JSON value (presumes the key has already been read) and set the /// underlying value. There's generally no reason to call this method /// directly. It is intended to be overridden by an extended type. /// </summary> /// <param name="path">path to the value being read</param> /// <param name="pbr">source reader</param> /// <exception cref="Gavaghan.JSON.JSONException">on grammar error</exception> /// <exception cref="System.IO.IOException">on read failure</exception> public virtual void Read(string path, PushbackReader pbr) { // assert we have an opening brace char c = JSONValueFactory.Demand(pbr); if (c != '{') { throw new JSONException(path, "Failed to find '{' at start of JSON object."); } for (; ;) { string key; // next is either a key or a closing brace mFactory.SkipWhitespace(pbr); c = JSONValueFactory.Demand(pbr); // is it a string? if (c == '\"') { pbr.Unread(c); key = JSONString.ReadString(path, pbr); } // is it a closing brace? else if (c == '}') { break; } // else, it's poorly formed else { throw new JSONException(path, "JSON object is not grammatically correct. Unexpected: " + c); } // next ought to be a colon mFactory.SkipWhitespace(pbr); c = JSONValueFactory.Demand(pbr); if (c != ':') { throw new JSONException(path + "." + key, "Expected ':' after key value"); } mFactory.SkipWhitespace(pbr); // next, read a JSONValue IJSONValue value = mFactory.Read(path + "." + key, pbr); // add it to the map Add(key, value); // next must be comma or close mFactory.SkipWhitespace(pbr); c = JSONValueFactory.Demand(pbr); if (c == ',') { continue; } if (c == '}') { break; } throw new JSONException(path, "JSON object is not grammatically correct. Unexpected: " + c); } mFactory = null; }