From 8a64988cb65ac34891d9efc1dd70bc817df1b60c Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sun, 2 Mar 2008 03:25:47 +0000 Subject: [PATCH] New features -New, more concise, LINQ to JSON querying syntax. -Added FromObject to JObject, JArray. -JToken WriteTo method now supports JsonConverters. -JsonSerializer supports serializing to and from LINQ to JSON objects. -JsonSerializer properly serializes IEnumerable objects. -Added Depth property to JsonReader. Changes -Made JsonReader and JsonWriter abstract. Added JsonTextReader/JsonNodeReader and JsonTextWriter/JsonNodeWriter. -Removed JavaScriptObject, JavaScriptArray. Replaced by LINQ to JSON objects. --- Src/Newtonsoft.Json/JavaScriptArray.cs | 64 -- Src/Newtonsoft.Json/JavaScriptConstructor.cs | 81 -- Src/Newtonsoft.Json/JavaScriptConvert.cs | 4 +- Src/Newtonsoft.Json/JavaScriptObject.cs | 55 -- Src/Newtonsoft.Json/JavaScriptParameters.cs | 42 - Src/Newtonsoft.Json/JsonReader.cs | 809 +----------------- Src/Newtonsoft.Json/JsonSerializer.cs | 94 +- Src/Newtonsoft.Json/JsonTextReader.cs | 755 ++++++++++++++++ Src/Newtonsoft.Json/JsonTextWriter.cs | 441 ++++++++++ Src/Newtonsoft.Json/JsonWriter.cs | 389 ++++----- Src/Newtonsoft.Json/Linq/Extensions.cs | 73 +- Src/Newtonsoft.Json/Linq/JArray.cs | 36 +- Src/Newtonsoft.Json/Linq/JConstructor.cs | 17 +- Src/Newtonsoft.Json/Linq/JContainer.cs | 48 +- Src/Newtonsoft.Json/Linq/JEnumerable.cs | 38 + Src/Newtonsoft.Json/Linq/JObject.cs | 49 +- Src/Newtonsoft.Json/Linq/JProperty.cs | 6 +- Src/Newtonsoft.Json/Linq/JToken.cs | 46 +- Src/Newtonsoft.Json/Linq/JValue.cs | 41 +- Src/Newtonsoft.Json/Linq/JsonTokenReader.cs | 159 ++++ Src/Newtonsoft.Json/Linq/JsonTokenType.cs | 1 + Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs | 306 +++++++ Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 9 +- .../Utilities/MiscellaneousUtils.cs | 5 +- 24 files changed, 2227 insertions(+), 1341 deletions(-) delete mode 100644 Src/Newtonsoft.Json/JavaScriptArray.cs delete mode 100644 Src/Newtonsoft.Json/JavaScriptConstructor.cs delete mode 100644 Src/Newtonsoft.Json/JavaScriptObject.cs delete mode 100644 Src/Newtonsoft.Json/JavaScriptParameters.cs create mode 100644 Src/Newtonsoft.Json/JsonTextReader.cs create mode 100644 Src/Newtonsoft.Json/JsonTextWriter.cs create mode 100644 Src/Newtonsoft.Json/Linq/JEnumerable.cs create mode 100644 Src/Newtonsoft.Json/Linq/JsonTokenReader.cs create mode 100644 Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs diff --git a/Src/Newtonsoft.Json/JavaScriptArray.cs b/Src/Newtonsoft.Json/JavaScriptArray.cs deleted file mode 100644 index 6456cdd7..00000000 --- a/Src/Newtonsoft.Json/JavaScriptArray.cs +++ /dev/null @@ -1,64 +0,0 @@ -#region License -// Copyright (c) 2007 James Newton-King -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Newtonsoft.Json -{ - /// - /// Represents a JavaScript array. - /// - public class JavaScriptArray : List - { - /// - /// Initializes a new instance of the class. - /// - public JavaScriptArray() - { - } - - /// - /// Initializes a new instance of the class that - /// contains elements copied from the specified collection. - /// - /// The collection whose elements are copied to the new array. - public JavaScriptArray(IEnumerable collection) - : base(collection) - { - } - - /// - /// Initializes a new instance of the class that - /// is empty and has the specified initial capacity. - /// - /// The number of elements that the new array can initially store. - public JavaScriptArray(int capacity) - : base(capacity) - { - } - } -} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JavaScriptConstructor.cs b/Src/Newtonsoft.Json/JavaScriptConstructor.cs deleted file mode 100644 index dd5b2b43..00000000 --- a/Src/Newtonsoft.Json/JavaScriptConstructor.cs +++ /dev/null @@ -1,81 +0,0 @@ -#region License -// Copyright (c) 2007 James Newton-King -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Newtonsoft.Json -{ - /// - /// Represents a JavaScript constructor. - /// - public class JavaScriptConstructor - { - private string _name; - private JavaScriptParameters _parameters; - - public JavaScriptParameters Parameters - { - get { return _parameters; } - } - - public string Name - { - get { return _name; } - } - - public JavaScriptConstructor(string name, JavaScriptParameters parameters) - { - if (name == null) - throw new ArgumentNullException("name"); - - if (name.Length == 0) - throw new ArgumentException("Constructor name cannot be empty.", "name"); - - _name = name; - _parameters = parameters ?? JavaScriptParameters.Empty; - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - sb.Append("new "); - sb.Append(_name); - sb.Append("("); - if (_parameters != null) - { - for (int i = 0; i < _parameters.Count; i++) - { - sb.Append(_parameters[i]); - } - } - sb.Append(")"); - - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JavaScriptConvert.cs b/Src/Newtonsoft.Json/JavaScriptConvert.cs index 5bb46380..f91d0532 100644 --- a/Src/Newtonsoft.Json/JavaScriptConvert.cs +++ b/Src/Newtonsoft.Json/JavaScriptConvert.cs @@ -388,7 +388,7 @@ namespace Newtonsoft.Json } } - using (JsonWriter jsonWriter = new JsonWriter(sw)) + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) { //jsonWriter.Formatting = Formatting.Indented; jsonSerializer.Serialize(jsonWriter, value); @@ -472,7 +472,7 @@ namespace Newtonsoft.Json object deserializedValue; - using (JsonReader jsonReader = new JsonReader(sr)) + using (JsonReader jsonReader = new JsonTextReader(sr)) { deserializedValue = jsonSerializer.Deserialize(jsonReader, type); } diff --git a/Src/Newtonsoft.Json/JavaScriptObject.cs b/Src/Newtonsoft.Json/JavaScriptObject.cs deleted file mode 100644 index b1625bae..00000000 --- a/Src/Newtonsoft.Json/JavaScriptObject.cs +++ /dev/null @@ -1,55 +0,0 @@ -#region License -// Copyright (c) 2007 James Newton-King -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Newtonsoft.Json -{ - /// - /// Represents a JavaScript object. - /// - public class JavaScriptObject : Dictionary - { - /// - /// Initializes a new instance of the class. - /// - public JavaScriptObject() - : base(EqualityComparer.Default) - { - } - - /// - /// Initializes a new instance of the class that - /// contains values copied from the specified . - /// - /// The whose elements are copied to the new object. - public JavaScriptObject(JavaScriptObject javaScriptObject) - : base(javaScriptObject, EqualityComparer.Default) - { - } - } -} diff --git a/Src/Newtonsoft.Json/JavaScriptParameters.cs b/Src/Newtonsoft.Json/JavaScriptParameters.cs deleted file mode 100644 index ba0a2cd3..00000000 --- a/Src/Newtonsoft.Json/JavaScriptParameters.cs +++ /dev/null @@ -1,42 +0,0 @@ -#region License -// Copyright (c) 2007 James Newton-King -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; -using System.Collections.ObjectModel; - -namespace Newtonsoft.Json -{ - public class JavaScriptParameters : ReadOnlyCollection - { - public static readonly JavaScriptParameters Empty = new JavaScriptParameters(new List()); - - public JavaScriptParameters(IList list) - : base(list) - { - } - } -} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 8421c2db..7dea5e65 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -29,15 +29,16 @@ using System.Text; using System.IO; using System.Xml; using System.Globalization; +using Newtonsoft.Json.Linq; namespace Newtonsoft.Json { /// /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. /// - public class JsonReader : IDisposable + public abstract class JsonReader : IDisposable { - private enum State + protected enum State { Start, Complete, @@ -54,20 +55,22 @@ namespace Newtonsoft.Json Finished } - private TextReader _reader; - private char _currentChar; - // current Token data private JsonToken _token; private object _value; private Type _valueType; private char _quoteChar; - private StringBuffer _buffer; - //private StringBuilder _testBuffer; private State _currentState; + protected State CurrentState + { + get { return _currentState; } + private set { _currentState = value; } + } + private int _top; - private List _stack; + + private List _stack; /// /// Gets the quotation mark character used to enclose the value of a string. @@ -75,6 +78,7 @@ namespace Newtonsoft.Json public char QuoteChar { get { return _quoteChar; } + protected set { _quoteChar = value; } } /// @@ -101,409 +105,55 @@ namespace Newtonsoft.Json get { return _valueType; } } + public int Depth + { + get { return _top - 1; } + } + /// /// Initializes a new instance of the class with the specified . /// - /// The TextReader containing the XML data to read. - public JsonReader(TextReader reader) + public JsonReader() { - if (reader == null) - throw new ArgumentNullException("reader"); - - _reader = reader; - _buffer = new StringBuffer(4096); //_testBuffer = new StringBuilder(); _currentState = State.Start; - _stack = new List(); + _stack = new List(); _top = 0; - Push(JsonType.None); + Push(JsonTokenType.None); } - private void Push(JsonType value) + private void Push(JsonTokenType value) { _stack.Add(value); _top++; } - private JsonType Pop() + private JsonTokenType Pop() { - JsonType value = Peek(); + JsonTokenType value = Peek(); _stack.RemoveAt(_stack.Count - 1); _top--; return value; } - private JsonType Peek() + private JsonTokenType Peek() { return _stack[_top - 1]; } - private void ParseString(char quote) - { - bool stringTerminated = false; - bool hexNumber = false; - int hexCount = 0; - - while (!stringTerminated && MoveNext()) - { - if (hexNumber) - hexCount++; - - switch (_currentChar) - { - case '\\': - if (MoveNext()) - { - switch (_currentChar) - { - case 'b': - _buffer.Append('\b'); - break; - case 't': - _buffer.Append('\t'); - break; - case 'n': - _buffer.Append('\n'); - break; - case 'f': - _buffer.Append('\f'); - break; - case 'r': - _buffer.Append('\r'); - break; - case 'u': - // note the start of a hex character - hexNumber = true; - break; - default: - _buffer.Append(_currentChar); - break; - } - } - else - { - throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); - } - break; - case '"': - case '\'': - if (_currentChar == quote) - stringTerminated = true; - else - goto default; - break; - default: - _buffer.Append(_currentChar); - break; - } - - if (hexCount == 4) - { - // remove hex characters from buffer, convert to char and then add - string hexString = _buffer.ToString(_buffer.Position - 4, 4); - char hexChar = Convert.ToChar(int.Parse(hexString, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); - - _buffer.Position = _buffer.Position - 4; - _buffer.Append(hexChar); - - hexNumber = false; - hexCount = 0; - } - } - - if (!stringTerminated) - throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); - - ClearCurrentChar(); - _currentState = State.PostValue; - string text = _buffer.ToString(); - _buffer.Position = 0; - - if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal)) - { - ParseDate(text); - } - else - { - SetToken(JsonToken.String, text); - _quoteChar = quote; - } - } - - private void ParseDate(string text) - { - string value = text.Substring(6, text.Length - 8); - DateTimeKind kind = DateTimeKind.Utc; - - int index = value.IndexOf('+', 1); - - if (index == -1) - index = value.IndexOf('-', 1); - - if (index != -1) - { - kind = DateTimeKind.Local; - value = value.Substring(0, index); - } - - long javaScriptTicks = long.Parse(value); - DateTime utcDateTime = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); - DateTime dateTime; - - switch (kind) - { - case DateTimeKind.Unspecified: - dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); - break; - case DateTimeKind.Local: - dateTime = utcDateTime.ToLocalTime(); - break; - default: - dateTime = utcDateTime; - break; - } - - SetToken(JsonToken.Date, dateTime); - } - - private bool MoveNext() - { - int value = _reader.Read(); - - if (value != -1) - { - _currentChar = (char)value; - //_testBuffer.Append(_currentChar); - return true; - } - else - { - return false; - } - } - - private bool HasNext() - { - return (_reader.Peek() != -1); - } - - private char PeekNext() - { - return (char)_reader.Peek(); - } - - private void ClearCurrentChar() - { - _currentChar = '\0'; - } - - private bool MoveTo(char value) - { - while (MoveNext()) - { - if (_currentChar == value) - return true; - } - return false; - } - /// /// Reads the next Json token from the stream. /// /// - public bool Read() - { - while (true) - { - if (_currentChar == '\0') - { - if (!MoveNext()) - return false; - } + public abstract bool Read(); - switch (_currentState) - { - case State.Start: - case State.Property: - case State.Array: - case State.ArrayStart: - case State.Constructor: - case State.ConstructorStart: - return ParseValue(); - case State.Complete: - break; - case State.Object: - case State.ObjectStart: - return ParseObject(); - case State.PostValue: - // returns true if it hits - // end of object or array - if (ParsePostValue()) - return true; - break; - case State.Closed: - break; - case State.Error: - break; - default: - throw new JsonReaderException("Unexpected state: " + _currentState); - } - } - } - - private bool ParsePostValue() - { - do - { - switch (_currentChar) - { - case '}': - SetToken(JsonToken.EndObject); - ClearCurrentChar(); - return true; - case ']': - SetToken(JsonToken.EndArray); - ClearCurrentChar(); - return true; - case ')': - SetToken(JsonToken.EndConstructor); - ClearCurrentChar(); - return true; - case '/': - ParseComment(); - return true; - case ',': - // finished paring - SetStateBasedOnCurrent(); - ClearCurrentChar(); - return false; - default: - if (char.IsWhiteSpace(_currentChar)) - { - // eat whitespace - ClearCurrentChar(); - } - else - { - throw new JsonReaderException("After parsing a value an unexpected character was encoutered: " + _currentChar); - } - break; - } - } while (MoveNext()); - - return false; - } - - private bool ParseObject() - { - do - { - switch (_currentChar) - { - case '}': - SetToken(JsonToken.EndObject); - return true; - case '/': - ParseComment(); - return true; - case ',': - SetToken(JsonToken.Undefined); - return true; - default: - if (char.IsWhiteSpace(_currentChar)) - { - // eat - } - else - { - return ParseProperty(); - } - break; - } - } while (MoveNext()); - - return false; - } - - private bool ParseProperty() - { - if (ValidIdentifierChar(_currentChar)) - { - ParseUnquotedProperty(); - } - else if (_currentChar == '"' || _currentChar == '\'') - { - ParseQuotedProperty(_currentChar); - } - else - { - throw new JsonReaderException("Invalid property identifier character: " + _currentChar); - } - - // finished property. move to colon - if (_currentChar != ':') - { - MoveTo(':'); - } - - SetToken(JsonToken.PropertyName, _buffer.ToString()); - _buffer.Position = 0; - - return true; - } - - private void ParseQuotedProperty(char quoteChar) - { - // parse property name until quoted char is hit - while (MoveNext()) - { - if (_currentChar == quoteChar) - { - return; - } - else - { - _buffer.Append(_currentChar); - } - } - - throw new JsonReaderException("Unclosed quoted property. Expected: " + quoteChar); - } - - private bool ValidIdentifierChar(char value) - { - return (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$'); - } - - private void ParseUnquotedProperty() - { - // parse unquoted property name until whitespace or colon - _buffer.Append(_currentChar); - - while (MoveNext()) - { - if (char.IsWhiteSpace(_currentChar) || _currentChar == ':') - { - break; - } - else if (ValidIdentifierChar(_currentChar)) - { - _buffer.Append(_currentChar); - } - else - { - throw new JsonReaderException("Invalid JavaScript property identifier character: " + _currentChar); - } - } - } - - private void SetToken(JsonToken newToken) + protected void SetToken(JsonToken newToken) { SetToken(newToken, null); } - private void SetToken(JsonToken newToken, object value) + protected virtual void SetToken(JsonToken newToken, object value) { _token = newToken; @@ -511,37 +161,30 @@ namespace Newtonsoft.Json { case JsonToken.StartObject: _currentState = State.ObjectStart; - Push(JsonType.Object); - ClearCurrentChar(); + Push(JsonTokenType.Object); break; case JsonToken.StartArray: _currentState = State.ArrayStart; - Push(JsonType.Array); - ClearCurrentChar(); + Push(JsonTokenType.Array); break; case JsonToken.StartConstructor: _currentState = State.ConstructorStart; - Push(JsonType.Constructor); - ClearCurrentChar(); + Push(JsonTokenType.Constructor); break; case JsonToken.EndObject: ValidateEnd(JsonToken.EndObject); - ClearCurrentChar(); _currentState = State.PostValue; break; case JsonToken.EndArray: ValidateEnd(JsonToken.EndArray); - ClearCurrentChar(); _currentState = State.PostValue; break; case JsonToken.EndConstructor: ValidateEnd(JsonToken.EndConstructor); - ClearCurrentChar(); _currentState = State.PostValue; break; case JsonToken.PropertyName: _currentState = State.Property; - ClearCurrentChar(); break; case JsonToken.Undefined: case JsonToken.Integer: @@ -549,6 +192,7 @@ namespace Newtonsoft.Json case JsonToken.Boolean: case JsonToken.Null: case JsonToken.Date: + case JsonToken.String: _currentState = State.PostValue; break; } @@ -565,242 +209,30 @@ namespace Newtonsoft.Json } } - private bool ParseValue() - { - do - { - switch (_currentChar) - { - case '"': - case '\'': - ParseString(_currentChar); - return true; - case 't': - ParseTrue(); - return true; - case 'f': - ParseFalse(); - return true; - case 'n': - if (HasNext()) - { - char next = PeekNext(); - - if (next == 'u') - ParseNull(); - else if (next == 'e') - ParseConstructor(); - else - throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); - } - else - { - throw new JsonReaderException("Unexpected end"); - } - return true; - case '/': - ParseComment(); - return true; - case 'u': - ParseUndefined(); - return true; - case '{': - SetToken(JsonToken.StartObject); - return true; - case '[': - SetToken(JsonToken.StartArray); - return true; - case '}': - SetToken(JsonToken.EndObject); - return true; - case ']': - SetToken(JsonToken.EndArray); - return true; - case ',': - SetToken(JsonToken.Undefined); - //ClearCurrentChar(); - return true; - case ')': - SetToken(JsonToken.EndConstructor); - return true; - //if (_currentState == State.Constructor) - //{ - // _currentState = State.ConstructorEnd; - // return false; - //} - //else - //{ - // throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); - //} - default: - if (char.IsWhiteSpace(_currentChar)) - { - // eat - } - else if (char.IsNumber(_currentChar) || _currentChar == '-' || _currentChar == '.') - { - ParseNumber(); - return true; - } - else - { - throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); - } - break; - } - } while (MoveNext()); - - return false; - } - - private bool EatWhitespace(bool oneOrMore) - { - bool whitespace = false; - while (char.IsWhiteSpace(_currentChar)) - { - whitespace = true; - MoveNext(); - } - - return (!oneOrMore || whitespace); - } - - private void ParseConstructor() - { - if (MatchValue("new", true)) - { - if (EatWhitespace(true)) - { - while (char.IsLetter(_currentChar)) - { - _buffer.Append(_currentChar); - MoveNext(); - } - - - EatWhitespace(false); - - if (_currentChar != '(') - throw new JsonReaderException("Unexpected character while parsing constructor: " + _currentChar); - - string constructorName = _buffer.ToString(); - _buffer.Position = 0; - - SetToken(JsonToken.StartConstructor); - - _value = constructorName; - _valueType = typeof(string); - - //if (string.CompareOrdinal(constructorName, "Date") == 0) - //{ - // IList parameters = new List(); - - // MoveNext(); - // while (ParseValue() && _token != JsonToken.EndConstructor) - // { - // parameters.Add(_value); - // } - - // long javaScriptTicks = Convert.ToInt64(parameters[0]); - - // DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); - - // SetToken(JsonToken.Date, date); - //} - - - - //{ - // _currentState = State.Constructor; - - // while (ParseValue()) - // { - // parameters.Add(_value); - // _currentState = State.Constructor; - // } - - // if (string.CompareOrdinal(constructorName, "Date") == 0) - // { - // long javaScriptTicks = Convert.ToInt64(parameters[0]); - - // DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); - - // SetToken(JsonToken.Date, date); - // } - // else - // { - // JavaScriptConstructor constructor = new JavaScriptConstructor(constructorName, new JavaScriptParameters(parameters)); - - // if (_currentState == State.ConstructorEnd) - // { - // SetToken(JsonToken.Constructor, constructor); - // } - // } - - // // move past ')' - // MoveNext(); - //} - } - } - } - - private void ParseNumber() - { - // parse until seperator character or end - bool end = false; - do - { - if (CurrentIsSeperator()) - end = true; - else - _buffer.Append(_currentChar); - - } while (!end && MoveNext()); - - string number = _buffer.ToString(); - object numberValue; - JsonToken numberType; - - if (number.IndexOf('.') == -1) - { - numberValue = Convert.ToInt64(_buffer.ToString(), CultureInfo.InvariantCulture); - numberType = JsonToken.Integer; - } - else - { - numberValue = Convert.ToDouble(_buffer.ToString(), CultureInfo.InvariantCulture); - numberType = JsonToken.Float; - } - - _buffer.Position = 0; - - SetToken(numberType, numberValue); - } - private void ValidateEnd(JsonToken endToken) { - JsonType currentObject = Pop(); + JsonTokenType currentObject = Pop(); if (GetTypeForCloseToken(endToken) != currentObject) throw new JsonReaderException(string.Format("JsonToken {0} is not valid for closing JsonType {1}.", endToken, currentObject)); } - - private void SetStateBasedOnCurrent() + + protected void SetStateBasedOnCurrent() { - JsonType currentObject = Peek(); + JsonTokenType currentObject = Peek(); switch (currentObject) { - case JsonType.Object: + case JsonTokenType.Object: _currentState = State.Object; break; - case JsonType.Array: + case JsonTokenType.Array: _currentState = State.Array; break; - case JsonType.Constructor: + case JsonTokenType.Constructor: _currentState = State.Constructor; break; - case JsonType.None: + case JsonTokenType.None: _currentState = State.Finished; break; default: @@ -808,172 +240,27 @@ namespace Newtonsoft.Json } } - private JsonType GetTypeForCloseToken(JsonToken token) + private JsonTokenType GetTypeForCloseToken(JsonToken token) { switch (token) { case JsonToken.EndObject: - return JsonType.Object; + return JsonTokenType.Object; case JsonToken.EndArray: - return JsonType.Array; + return JsonTokenType.Array; case JsonToken.EndConstructor: - return JsonType.Constructor; + return JsonTokenType.Constructor; default: throw new JsonReaderException("Not a valid close JsonToken: " + token); } } - private void ParseComment() - { - // should have already parsed / character before reaching this method - - MoveNext(); - - if (_currentChar == '*') - { - while (MoveNext()) - { - if (_currentChar == '*') - { - if (MoveNext()) - { - if (_currentChar == '/') - { - break; - } - else - { - _buffer.Append('*'); - _buffer.Append(_currentChar); - } - } - } - else - { - _buffer.Append(_currentChar); - } - } - } - else - { - throw new JsonReaderException("Error parsing comment. Expected: *"); - } - - SetToken(JsonToken.Comment, _buffer.ToString()); - - _buffer.Position = 0; - - ClearCurrentChar(); - } - - private bool MatchValue(string value) - { - int i = 0; - do - { - if (_currentChar != value[i]) - { - break; - } - i++; - } - while (i < value.Length && MoveNext()); - - return (i == value.Length); - } - - private bool MatchValue(string value, bool noTrailingNonSeperatorCharacters) - { - // will match value and then move to the next character, checking that it is a seperator character - bool match = MatchValue(value); - - if (!noTrailingNonSeperatorCharacters) - return match; - else - return (match && (!MoveNext() || CurrentIsSeperator())); - } - - private bool CurrentIsSeperator() - { - switch (_currentChar) - { - case '}': - case ']': - case ',': - return true; - case '/': - // check next character to see if start of a comment - return (HasNext() && PeekNext() == '*'); - case ')': - if (_currentState == State.Constructor || _currentState == State.ConstructorStart) - return true; - break; - default: - if (char.IsWhiteSpace(_currentChar)) - return true; - break; - } - - return false; - } - - private void ParseTrue() - { - // check characters equal 'true' - // and that it is followed by either a seperator character - // or the text ends - if (MatchValue(JavaScriptConvert.True, true)) - { - SetToken(JsonToken.Boolean, true); - } - else - { - throw new JsonReaderException("Error parsing boolean value."); - } - } - - private void ParseNull() - { - if (MatchValue(JavaScriptConvert.Null, true)) - { - SetToken(JsonToken.Null); - } - else - { - throw new JsonReaderException("Error parsing null value."); - } - } - - private void ParseUndefined() - { - if (MatchValue(JavaScriptConvert.Undefined, true)) - { - SetToken(JsonToken.Undefined); - } - else - { - throw new JsonReaderException("Error parsing undefined value."); - } - } - - private void ParseFalse() - { - if (MatchValue(JavaScriptConvert.False, true)) - { - SetToken(JsonToken.Boolean, false); - } - else - { - throw new JsonReaderException("Error parsing boolean value."); - } - } - void IDisposable.Dispose() { Dispose(true); } - private void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) { if (_currentState != State.Closed && disposing) Close(); @@ -982,18 +269,12 @@ namespace Newtonsoft.Json /// /// Changes the to Closed. /// - public void Close() + public virtual void Close() { _currentState = State.Closed; _token = JsonToken.None; _value = null; _valueType = null; - - if (_reader != null) - _reader.Close(); - - if (_buffer != null) - _buffer.Clear(); } } } diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 1f9d1932..633d00c6 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -33,6 +33,7 @@ using System.ComponentModel; using Newtonsoft.Json.Utilities; using System.Globalization; using System.Linq; +using Newtonsoft.Json.Linq; namespace Newtonsoft.Json { @@ -140,64 +141,22 @@ namespace Newtonsoft.Json if (!reader.Read()) return null; - return GetObject(reader, objectType); + if (objectType != null) + return GetObject(reader, objectType); + else + return CreateJToken(reader); } - private JavaScriptArray PopulateJavaScriptArray(JsonReader reader) + private JToken CreateJToken(JsonReader reader) { - JavaScriptArray jsArray = new JavaScriptArray(); - - while (reader.Read()) + JToken token; + using (JsonTokenWriter writer = new JsonTokenWriter()) { - switch (reader.TokenType) - { - case JsonToken.EndArray: - return jsArray; - case JsonToken.Comment: - break; - default: - object value = GetObject(reader, null); - - jsArray.Add(value); - break; - } + writer.WriteToken(reader); + token = writer.Token; } - throw new JsonSerializationException("Unexpected end while deserializing array."); - } - - private JavaScriptObject PopulateJavaScriptObject(JsonReader reader) - { - JavaScriptObject jsObject = new JavaScriptObject(); - - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonToken.PropertyName: - string memberName = reader.Value.ToString(); - - // move to the value token. skip comments - do - { - if (!reader.Read()) - throw new JsonSerializationException("Unexpected end while deserializing object."); - } while (reader.TokenType == JsonToken.Comment); - - object value = GetObject(reader, null); - - jsObject[memberName] = value; - break; - case JsonToken.EndObject: - return jsObject; - case JsonToken.Comment: - break; - default: - throw new JsonSerializationException("Unexpected token while deserializing object: " + reader.TokenType); - } - } - - throw new JsonSerializationException("Unexpected end while deserializing object."); + return token; } private object GetObject(JsonReader reader, Type objectType) @@ -218,10 +177,10 @@ namespace Newtonsoft.Json // populate a typed object or generic dictionary/array // depending upon whether an objectType was supplied case JsonToken.StartObject: - value = (objectType != null) ? PopulateObject(reader, objectType) : PopulateJavaScriptObject(reader); + value = (objectType != null) ? PopulateObject(reader, objectType) : CreateJToken(reader); break; case JsonToken.StartArray: - value = (objectType != null) ? PopulateList(reader, objectType) : PopulateJavaScriptArray(reader); + value = (objectType != null) ? PopulateList(reader, objectType) : CreateJToken(reader); break; case JsonToken.Integer: case JsonToken.Float: @@ -517,7 +476,7 @@ namespace Newtonsoft.Json /// The to serialize. public void Serialize(TextWriter textWriter, object value) { - Serialize(new JsonWriter(textWriter), value); + Serialize(new JsonTextWriter(textWriter), value); } /// @@ -531,7 +490,10 @@ namespace Newtonsoft.Json if (jsonWriter == null) throw new ArgumentNullException("jsonWriter"); - SerializeValue(jsonWriter, value); + if (value is JToken) + ((JToken)value).WriteTo(jsonWriter, (_converters != null) ? _converters.ToArray() : null); + else + SerializeValue(jsonWriter, value); } @@ -619,6 +581,10 @@ namespace Newtonsoft.Json { SerializeCollection(writer, (ICollection)value); } + else if (value is IEnumerable) + { + SerializeEnumerable(writer, (IEnumerable)value); + } else if (value is Identifier) { writer.WriteRaw(value.ToString()); @@ -631,11 +597,16 @@ namespace Newtonsoft.Json private bool HasMatchingConverter(Type type, out JsonConverter matchingConverter) { - if (_converters != null) + return HasMatchingConverter(_converters, type, out matchingConverter); + } + + internal static bool HasMatchingConverter(IList converters, Type type, out JsonConverter matchingConverter) + { + if (converters != null) { - for (int i = 0; i < _converters.Count; i++) + for (int i = 0; i < converters.Count; i++) { - JsonConverter converter = _converters[i]; + JsonConverter converter = converters[i]; if (converter.CanConvert(type)) { @@ -713,6 +684,11 @@ namespace Newtonsoft.Json writer.SerializeStack.Remove(value); } + private void SerializeEnumerable(JsonWriter writer, IEnumerable values) + { + SerializeList(writer, values.Cast().ToList()); + } + private void SerializeCollection(JsonWriter writer, ICollection values) { object[] collectionValues = new object[values.Count]; diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs new file mode 100644 index 00000000..1a8d9f35 --- /dev/null +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -0,0 +1,755 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Xml; +using System.Globalization; + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class JsonTextReader : JsonReader + { + private TextReader _reader; + private char _currentChar; + + // current Token data + private StringBuffer _buffer; + + /// + /// Initializes a new instance of the class with the specified . + /// + /// The TextReader containing the XML data to read. + public JsonTextReader(TextReader reader) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + _reader = reader; + _buffer = new StringBuffer(4096); + } + + private void ParseString(char quote) + { + bool stringTerminated = false; + bool hexNumber = false; + int hexCount = 0; + + while (!stringTerminated && MoveNext()) + { + if (hexNumber) + hexCount++; + + switch (_currentChar) + { + case '\\': + if (MoveNext()) + { + switch (_currentChar) + { + case 'b': + _buffer.Append('\b'); + break; + case 't': + _buffer.Append('\t'); + break; + case 'n': + _buffer.Append('\n'); + break; + case 'f': + _buffer.Append('\f'); + break; + case 'r': + _buffer.Append('\r'); + break; + case 'u': + // note the start of a hex character + hexNumber = true; + break; + default: + _buffer.Append(_currentChar); + break; + } + } + else + { + throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); + } + break; + case '"': + case '\'': + if (_currentChar == quote) + stringTerminated = true; + else + goto default; + break; + default: + _buffer.Append(_currentChar); + break; + } + + if (hexCount == 4) + { + // remove hex characters from buffer, convert to char and then add + string hexString = _buffer.ToString(_buffer.Position - 4, 4); + char hexChar = Convert.ToChar(int.Parse(hexString, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); + + _buffer.Position = _buffer.Position - 4; + _buffer.Append(hexChar); + + hexNumber = false; + hexCount = 0; + } + } + + if (!stringTerminated) + throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); + + ClearCurrentChar(); + string text = _buffer.ToString(); + _buffer.Position = 0; + + if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal)) + { + ParseDate(text); + } + else + { + SetToken(JsonToken.String, text); + QuoteChar = quote; + } + } + + protected override void SetToken(JsonToken newToken, object value) + { + base.SetToken(newToken, value); + + switch (newToken) + { + case JsonToken.StartObject: + ClearCurrentChar(); + break; + case JsonToken.StartArray: + ClearCurrentChar(); + break; + case JsonToken.StartConstructor: + ClearCurrentChar(); + break; + case JsonToken.EndObject: + ClearCurrentChar(); + break; + case JsonToken.EndArray: + ClearCurrentChar(); + break; + case JsonToken.EndConstructor: + ClearCurrentChar(); + break; + case JsonToken.PropertyName: + ClearCurrentChar(); + break; + } + } + + private void ParseDate(string text) + { + string value = text.Substring(6, text.Length - 8); + DateTimeKind kind = DateTimeKind.Utc; + + int index = value.IndexOf('+', 1); + + if (index == -1) + index = value.IndexOf('-', 1); + + if (index != -1) + { + kind = DateTimeKind.Local; + value = value.Substring(0, index); + } + + long javaScriptTicks = long.Parse(value); + DateTime utcDateTime = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); + DateTime dateTime; + + switch (kind) + { + case DateTimeKind.Unspecified: + dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); + break; + case DateTimeKind.Local: + dateTime = utcDateTime.ToLocalTime(); + break; + default: + dateTime = utcDateTime; + break; + } + + SetToken(JsonToken.Date, dateTime); + } + + private bool MoveNext() + { + int value = _reader.Read(); + + if (value != -1) + { + _currentChar = (char)value; + //_testBuffer.Append(_currentChar); + return true; + } + else + { + return false; + } + } + + private bool HasNext() + { + return (_reader.Peek() != -1); + } + + private char PeekNext() + { + return (char)_reader.Peek(); + } + + private void ClearCurrentChar() + { + _currentChar = '\0'; + } + + private bool MoveTo(char value) + { + while (MoveNext()) + { + if (_currentChar == value) + return true; + } + return false; + } + + /// + /// Reads the next Json token from the stream. + /// + /// + public override bool Read() + { + while (true) + { + if (_currentChar == '\0') + { + if (!MoveNext()) + return false; + } + + switch (CurrentState) + { + case State.Start: + case State.Property: + case State.Array: + case State.ArrayStart: + case State.Constructor: + case State.ConstructorStart: + return ParseValue(); + case State.Complete: + break; + case State.Object: + case State.ObjectStart: + return ParseObject(); + case State.PostValue: + // returns true if it hits + // end of object or array + if (ParsePostValue()) + return true; + break; + case State.Closed: + break; + case State.Error: + break; + default: + throw new JsonReaderException("Unexpected state: " + CurrentState); + } + } + } + + private bool ParsePostValue() + { + do + { + switch (_currentChar) + { + case '}': + SetToken(JsonToken.EndObject); + ClearCurrentChar(); + return true; + case ']': + SetToken(JsonToken.EndArray); + ClearCurrentChar(); + return true; + case ')': + SetToken(JsonToken.EndConstructor); + ClearCurrentChar(); + return true; + case '/': + ParseComment(); + return true; + case ',': + // finished paring + SetStateBasedOnCurrent(); + ClearCurrentChar(); + return false; + default: + if (char.IsWhiteSpace(_currentChar)) + { + // eat whitespace + ClearCurrentChar(); + } + else + { + throw new JsonReaderException("After parsing a value an unexpected character was encoutered: " + _currentChar); + } + break; + } + } while (MoveNext()); + + return false; + } + + private bool ParseObject() + { + do + { + switch (_currentChar) + { + case '}': + SetToken(JsonToken.EndObject); + return true; + case '/': + ParseComment(); + return true; + case ',': + SetToken(JsonToken.Undefined); + return true; + default: + if (char.IsWhiteSpace(_currentChar)) + { + // eat + } + else + { + return ParseProperty(); + } + break; + } + } while (MoveNext()); + + return false; + } + + private bool ParseProperty() + { + if (ValidIdentifierChar(_currentChar)) + { + ParseUnquotedProperty(); + } + else if (_currentChar == '"' || _currentChar == '\'') + { + ParseQuotedProperty(_currentChar); + } + else + { + throw new JsonReaderException("Invalid property identifier character: " + _currentChar); + } + + // finished property. move to colon + if (_currentChar != ':') + { + MoveTo(':'); + } + + SetToken(JsonToken.PropertyName, _buffer.ToString()); + _buffer.Position = 0; + + return true; + } + + private void ParseQuotedProperty(char quoteChar) + { + // parse property name until quoted char is hit + while (MoveNext()) + { + if (_currentChar == quoteChar) + { + return; + } + else + { + _buffer.Append(_currentChar); + } + } + + throw new JsonReaderException("Unclosed quoted property. Expected: " + quoteChar); + } + + private bool ValidIdentifierChar(char value) + { + return (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$'); + } + + private void ParseUnquotedProperty() + { + // parse unquoted property name until whitespace or colon + _buffer.Append(_currentChar); + + while (MoveNext()) + { + if (char.IsWhiteSpace(_currentChar) || _currentChar == ':') + { + break; + } + else if (ValidIdentifierChar(_currentChar)) + { + _buffer.Append(_currentChar); + } + else + { + throw new JsonReaderException("Invalid JavaScript property identifier character: " + _currentChar); + } + } + } + + private bool ParseValue() + { + do + { + switch (_currentChar) + { + case '"': + case '\'': + ParseString(_currentChar); + return true; + case 't': + ParseTrue(); + return true; + case 'f': + ParseFalse(); + return true; + case 'n': + if (HasNext()) + { + char next = PeekNext(); + + if (next == 'u') + ParseNull(); + else if (next == 'e') + ParseConstructor(); + else + throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); + } + else + { + throw new JsonReaderException("Unexpected end"); + } + return true; + case '/': + ParseComment(); + return true; + case 'u': + ParseUndefined(); + return true; + case '{': + SetToken(JsonToken.StartObject); + return true; + case '[': + SetToken(JsonToken.StartArray); + return true; + case '}': + SetToken(JsonToken.EndObject); + return true; + case ']': + SetToken(JsonToken.EndArray); + return true; + case ',': + SetToken(JsonToken.Undefined); + //ClearCurrentChar(); + return true; + case ')': + SetToken(JsonToken.EndConstructor); + return true; + default: + if (char.IsWhiteSpace(_currentChar)) + { + // eat + } + else if (char.IsNumber(_currentChar) || _currentChar == '-' || _currentChar == '.') + { + ParseNumber(); + return true; + } + else + { + throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); + } + break; + } + } while (MoveNext()); + + return false; + } + + private bool EatWhitespace(bool oneOrMore) + { + bool whitespace = false; + while (char.IsWhiteSpace(_currentChar)) + { + whitespace = true; + MoveNext(); + } + + return (!oneOrMore || whitespace); + } + + private void ParseConstructor() + { + if (MatchValue("new", true)) + { + if (EatWhitespace(true)) + { + while (char.IsLetter(_currentChar)) + { + _buffer.Append(_currentChar); + MoveNext(); + } + + EatWhitespace(false); + + if (_currentChar != '(') + throw new JsonReaderException("Unexpected character while parsing constructor: " + _currentChar); + + string constructorName = _buffer.ToString(); + _buffer.Position = 0; + + SetToken(JsonToken.StartConstructor, constructorName); + } + } + } + + private void ParseNumber() + { + // parse until seperator character or end + bool end = false; + do + { + if (CurrentIsSeperator()) + end = true; + else + _buffer.Append(_currentChar); + + } while (!end && MoveNext()); + + string number = _buffer.ToString(); + object numberValue; + JsonToken numberType; + + if (number.IndexOf('.') == -1) + { + numberValue = Convert.ToInt64(_buffer.ToString(), CultureInfo.InvariantCulture); + numberType = JsonToken.Integer; + } + else + { + numberValue = Convert.ToDouble(_buffer.ToString(), CultureInfo.InvariantCulture); + numberType = JsonToken.Float; + } + + _buffer.Position = 0; + + SetToken(numberType, numberValue); + } + + private void ParseComment() + { + // should have already parsed / character before reaching this method + + MoveNext(); + + if (_currentChar == '*') + { + while (MoveNext()) + { + if (_currentChar == '*') + { + if (MoveNext()) + { + if (_currentChar == '/') + { + break; + } + else + { + _buffer.Append('*'); + _buffer.Append(_currentChar); + } + } + } + else + { + _buffer.Append(_currentChar); + } + } + } + else + { + throw new JsonReaderException("Error parsing comment. Expected: *"); + } + + SetToken(JsonToken.Comment, _buffer.ToString()); + + _buffer.Position = 0; + + ClearCurrentChar(); + } + + private bool MatchValue(string value) + { + int i = 0; + do + { + if (_currentChar != value[i]) + { + break; + } + i++; + } + while (i < value.Length && MoveNext()); + + return (i == value.Length); + } + + private bool MatchValue(string value, bool noTrailingNonSeperatorCharacters) + { + // will match value and then move to the next character, checking that it is a seperator character + bool match = MatchValue(value); + + if (!noTrailingNonSeperatorCharacters) + return match; + else + return (match && (!MoveNext() || CurrentIsSeperator())); + } + + private bool CurrentIsSeperator() + { + switch (_currentChar) + { + case '}': + case ']': + case ',': + return true; + case '/': + // check next character to see if start of a comment + return (HasNext() && PeekNext() == '*'); + case ')': + if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart) + return true; + break; + default: + if (char.IsWhiteSpace(_currentChar)) + return true; + break; + } + + return false; + } + + private void ParseTrue() + { + // check characters equal 'true' + // and that it is followed by either a seperator character + // or the text ends + if (MatchValue(JavaScriptConvert.True, true)) + { + SetToken(JsonToken.Boolean, true); + } + else + { + throw new JsonReaderException("Error parsing boolean value."); + } + } + + private void ParseNull() + { + if (MatchValue(JavaScriptConvert.Null, true)) + { + SetToken(JsonToken.Null); + } + else + { + throw new JsonReaderException("Error parsing null value."); + } + } + + private void ParseUndefined() + { + if (MatchValue(JavaScriptConvert.Undefined, true)) + { + SetToken(JsonToken.Undefined); + } + else + { + throw new JsonReaderException("Error parsing undefined value."); + } + } + + private void ParseFalse() + { + if (MatchValue(JavaScriptConvert.False, true)) + { + SetToken(JsonToken.Boolean, false); + } + else + { + throw new JsonReaderException("Error parsing boolean value."); + } + } + + /// + /// Changes the to Closed. + /// + public void Close() + { + base.Close(); + + if (_reader != null) + _reader.Close(); + + if (_buffer != null) + _buffer.Clear(); + } + } +} diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs new file mode 100644 index 00000000..d7ff55f0 --- /dev/null +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -0,0 +1,441 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Xml; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public class JsonTextWriter : JsonWriter + { + private TextWriter _writer; + private char _indentChar; + private int _indentation; + private char _quoteChar; + private bool _quoteName; + + /// + /// Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. + /// + public int Indentation + { + get { return _indentation; } + set + { + if (value < 0) + throw new ArgumentException("Indentation value must be greater than 0."); + + _indentation = value; + } + } + + /// + /// Gets or sets which character to use to quote attribute values. + /// + public char QuoteChar + { + get { return _quoteChar; } + set + { + if (value != '"' && value != '\'') + throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and ""."); + + _quoteChar = value; + } + } + + /// + /// Gets or sets which character to use for indenting when is set to Formatting.Indented. + /// + public char IndentChar + { + get { return _indentChar; } + set { _indentChar = value; } + } + + /// + /// Gets or sets a value indicating whether object names will be surrounded with quotes. + /// + public bool QuoteName + { + get { return _quoteName; } + set { _quoteName = value; } + } + + /// + /// Creates an instance of the JsonWriter class using the specified . + /// + /// The TextWriter to write to. + public JsonTextWriter(TextWriter textWriter) + { + if (textWriter == null) + throw new ArgumentNullException("textWriter"); + + _writer = textWriter; + _quoteChar = '"'; + _quoteName = true; + _indentChar = ' '; + _indentation = 2; + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + _writer.Flush(); + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + + _writer.Close(); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + _writer.Write("{"); + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + _writer.Write("["); + } + + public override void WriteStartConstructor(string name) + { + base.WriteStartConstructor(name); + + _writer.Write("new "); + _writer.Write(name); + _writer.Write("("); + } + + protected override void WriteEnd(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + _writer.Write("}"); + break; + case JsonToken.EndArray: + _writer.Write("]"); + break; + case JsonToken.EndConstructor: + _writer.Write(")"); + break; + default: + throw new JsonWriterException("Invalid JsonToken: " + token); + } + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + if (_quoteName) + _writer.Write(_quoteChar); + + _writer.Write(name); + + if (_quoteName) + _writer.Write(_quoteChar); + + _writer.Write(':'); + } + + protected override void WriteIndent() + { + if (Formatting == Formatting.Indented) + { + _writer.Write(Environment.NewLine); + // for each level of object... + for (int i = 0; i < Top; i++) + { + // ...write the indent char the specified number of times + for (int j = 0; j < _indentation; j++) + { + _writer.Write(_indentChar); + } + } + } + } + + protected override void WriteValueDelimiter() + { + _writer.Write(','); + } + + protected override void WriteIndentSpace() + { + _writer.Write(' '); + } + + private void WriteValueInternal(string value, JsonToken token) + { + _writer.Write(value); + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + WriteValueInternal(JavaScriptConvert.Null, JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + WriteValueInternal(JavaScriptConvert.Undefined, JsonToken.Undefined); + } + + /// + /// Writes raw JavaScript manually. + /// + /// The raw JavaScript to write. + public override void WriteRaw(string javaScript) + { + base.WriteRaw(javaScript); + // hack. some 'raw' or 'other' token perhaps? + WriteValueInternal(javaScript, JsonToken.Undefined); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value, _quoteChar), JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(uint value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(ulong value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(ushort value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date); + } + #endregion + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + base.WriteComment(text); + + _writer.Write("/*"); + _writer.Write(text); + _writer.Write("*/"); + } + + /// + /// Writes out the given white space. + /// + /// The string of white space characters. + public override void WriteWhitespace(string ws) + { + base.WriteWhitespace(ws); + + _writer.Write(ws); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index f488e6bf..69e3f282 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -29,17 +29,10 @@ using System.Text; using System.IO; using System.Xml; using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; namespace Newtonsoft.Json { - internal enum JsonType - { - Object, - Array, - Constructor, - None - } - /// /// Specifies the state of the . /// @@ -95,7 +88,7 @@ namespace Newtonsoft.Json /// /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. /// - public class JsonWriter : IDisposable + public abstract class JsonWriter : IDisposable { private enum State { @@ -125,15 +118,11 @@ namespace Newtonsoft.Json }; private int _top; - private List _stack; + + private List _stack; private List _serializeStack; - private TextWriter _writer; - private Formatting _formatting; - private char _indentChar; - private int _indentation; - private char _quoteChar; - private bool _quoteName; private State _currentState; + private Formatting _formatting; internal List SerializeStack { @@ -146,6 +135,11 @@ namespace Newtonsoft.Json } } + protected int Top + { + get { return _top; } + } + /// /// Gets the state of the writer. /// @@ -187,75 +181,19 @@ namespace Newtonsoft.Json set { _formatting = value; } } - /// - /// Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. - /// - public int Indentation - { - get { return _indentation; } - set - { - if (value < 0) - throw new ArgumentException("Indentation value must be greater than 0."); - - _indentation = value; - } - } - - /// - /// Gets or sets which character to use to quote attribute values. - /// - public char QuoteChar - { - get { return _quoteChar; } - set - { - if (value != '"' && value != '\'') - throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and ""."); - - _quoteChar = value; - } - } - - /// - /// Gets or sets which character to use for indenting when is set to Formatting.Indented. - /// - public char IndentChar - { - get { return _indentChar; } - set { _indentChar = value; } - } - - /// - /// Gets or sets a value indicating whether object names will be surrounded with quotes. - /// - public bool QuoteName - { - get { return _quoteName; } - set { _quoteName = value; } - } - /// /// Creates an instance of the JsonWriter class using the specified . /// /// The TextWriter to write to. - public JsonWriter(TextWriter textWriter) + public JsonWriter() { - if (textWriter == null) - throw new ArgumentNullException("textWriter"); - - _writer = textWriter; - _quoteChar = '"'; - _quoteName = true; - _indentChar = ' '; - _indentation = 2; - _formatting = Formatting.None; - _stack = new List(1); - _stack.Add(JsonType.None); + _stack = new List(1); + _stack.Add(JsonTokenType.None); _currentState = State.Start; + _formatting = Formatting.None; } - private void Push(JsonType value) + private void Push(JsonTokenType value) { _top++; if (_stack.Count <= _top) @@ -264,15 +202,15 @@ namespace Newtonsoft.Json _stack[_top] = value; } - private JsonType Pop() + private JsonTokenType Pop() { - JsonType value = Peek(); + JsonTokenType value = Peek(); _top--; return value; } - private JsonType Peek() + private JsonTokenType Peek() { return _stack[_top]; } @@ -280,31 +218,23 @@ namespace Newtonsoft.Json /// /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. /// - public void Flush() - { - _writer.Flush(); - } + public abstract void Flush(); /// /// Closes this stream and the underlying stream. /// - public void Close() + public virtual void Close() { AutoCompleteAll(); - - _writer.Close(); } /// /// Writes the beginning of a Json object. /// - public void WriteStartObject() + public virtual void WriteStartObject() { AutoComplete(JsonToken.StartObject); - - Push(JsonType.Object); - - _writer.Write("{"); + Push(JsonTokenType.Object); } /// @@ -318,11 +248,10 @@ namespace Newtonsoft.Json /// /// Writes the beginning of a Json array. /// - public void WriteStartArray() + public virtual void WriteStartArray() { AutoComplete(JsonToken.StartArray); - Push(JsonType.Array); - _writer.Write("["); + Push(JsonTokenType.Array); } /// @@ -333,13 +262,10 @@ namespace Newtonsoft.Json AutoCompleteClose(JsonToken.EndArray); } - public void WriteStartConstructor(string name) + public virtual void WriteStartConstructor(string name) { AutoComplete(JsonToken.StartConstructor); - Push(JsonType.Constructor); - _writer.Write("new "); - _writer.Write(name); - _writer.Write("("); + Push(JsonTokenType.Constructor); } public void WriteEndConstructor() @@ -351,20 +277,9 @@ namespace Newtonsoft.Json /// Writes the property name of a name/value pair on a Json object. /// /// - public void WritePropertyName(string name) + public virtual void WritePropertyName(string name) { - //_objectStack.Push(new JsonObjectInfo(JsonType.Property)); AutoComplete(JsonToken.PropertyName); - - if (_quoteName) - _writer.Write(_quoteChar); - - _writer.Write(name); - - if (_quoteName) - _writer.Write(_quoteChar); - - _writer.Write(':'); } /// @@ -375,17 +290,95 @@ namespace Newtonsoft.Json WriteEnd(Peek()); } - private void WriteEnd(JsonType type) + public void WriteToken(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + int currentDepth = (reader.TokenType == JsonToken.None) ? -1 : reader.Depth; + bool continue1; + do + { + switch (reader.TokenType) + { + case JsonToken.None: + // read to next + break; + case JsonToken.StartObject: + WriteStartObject(); + break; + case JsonToken.StartArray: + WriteStartArray(); + break; + case JsonToken.StartConstructor: + WriteStartConstructor(reader.Value.ToString()); + break; + case JsonToken.PropertyName: + WritePropertyName(reader.Value.ToString()); + break; + case JsonToken.Comment: + WriteComment(reader.Value.ToString()); + break; + case JsonToken.Integer: + WriteValue((long)reader.Value); + break; + case JsonToken.Float: + WriteValue((double)reader.Value); + break; + case JsonToken.String: + WriteValue(reader.Value.ToString()); + break; + case JsonToken.Boolean: + WriteValue((bool)reader.Value); + break; + case JsonToken.Null: + WriteNull(); + break; + case JsonToken.Undefined: + WriteUndefined(); + break; + case JsonToken.EndObject: + WriteEndObject(); + break; + case JsonToken.EndArray: + WriteEndArray(); + break; + case JsonToken.EndConstructor: + WriteEndConstructor(); + break; + case JsonToken.Date: + WriteValue((DateTime)reader.Value); + break; + default: + throw new ArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type."); + } + } + while (reader.Read() && (currentDepth - 1 < reader.Depth || (currentDepth == reader.Depth && !IsEndToken(reader.TokenType)))); + } + + private bool IsEndToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + return true; + default: + return false; + } + } + + private void WriteEnd(JsonTokenType type) { switch (type) { - case JsonType.Object: + case JsonTokenType.Object: WriteEndObject(); break; - case JsonType.Array: + case JsonTokenType.Array: WriteEndArray(); break; - case JsonType.Constructor: + case JsonTokenType.Constructor: WriteEndConstructor(); break; default: @@ -401,30 +394,30 @@ namespace Newtonsoft.Json } } - private JsonType GetTypeForCloseToken(JsonToken token) + private JsonTokenType GetTypeForCloseToken(JsonToken token) { switch (token) { case JsonToken.EndObject: - return JsonType.Object; + return JsonTokenType.Object; case JsonToken.EndArray: - return JsonType.Array; + return JsonTokenType.Array; case JsonToken.EndConstructor: - return JsonType.Constructor; + return JsonTokenType.Constructor; default: throw new JsonWriterException("No type for token: " + token); } } - private JsonToken GetCloseTokenForType(JsonType type) + private JsonToken GetCloseTokenForType(JsonTokenType type) { switch (type) { - case JsonType.Object: + case JsonTokenType.Object: return JsonToken.EndObject; - case JsonType.Array: + case JsonTokenType.Array: return JsonToken.EndArray; - case JsonType.Constructor: + case JsonTokenType.Constructor: return JsonToken.EndConstructor; default: throw new JsonWriterException("No close token for type: " + type); @@ -458,33 +451,20 @@ namespace Newtonsoft.Json if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) WriteIndent(); - switch (token) - { - case JsonToken.EndObject: - _writer.Write("}"); - break; - case JsonToken.EndArray: - _writer.Write("]"); - break; - case JsonToken.EndConstructor: - _writer.Write(")"); - break; - default: - throw new JsonWriterException("Invalid JsonToken: " + token); - } + WriteEnd(token); } - JsonType currentLevelType = Peek(); + JsonTokenType currentLevelType = Peek(); switch (currentLevelType) { - case JsonType.Object: + case JsonTokenType.Object: _currentState = State.Object; break; - case JsonType.Array: + case JsonTokenType.Array: _currentState = State.Array; break; - case JsonType.None: + case JsonTokenType.None: _currentState = State.Start; break; default: @@ -492,21 +472,20 @@ namespace Newtonsoft.Json } } - private void WriteIndent() + protected virtual void WriteEnd(JsonToken token) + { + } + + protected virtual void WriteIndent() + { + } + + protected virtual void WriteValueDelimiter() + { + } + + protected virtual void WriteIndentSpace() { - if (_formatting == Formatting.Indented) - { - _writer.Write(Environment.NewLine); - // for each level of object... - for (int i = 0; i < _top; i++) - { - // ...write the indent char the specified number of times - for (int j = 0; j < _indentation; j++) - { - _writer.Write(_indentChar); - } - } - } } private void AutoComplete(JsonToken tokenBeingWritten) @@ -538,12 +517,12 @@ namespace Newtonsoft.Json if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment) { - _writer.Write(','); + WriteValueDelimiter(); } else if (_currentState == State.Property) { if (_formatting == Formatting.Indented) - _writer.Write(' '); + WriteIndentSpace(); } // don't indent a property when it is the first token to be written (i.e. at the start) @@ -556,182 +535,175 @@ namespace Newtonsoft.Json _currentState = newState; } - private void WriteValueInternal(string value, JsonToken token) - { - AutoComplete(token); - - _writer.Write(value); - } - #region WriteValue methods /// /// Writes a null value. /// - public void WriteNull() + public virtual void WriteNull() { - WriteValueInternal(JavaScriptConvert.Null, JsonToken.Null); + AutoComplete(JsonToken.Null); } /// /// Writes an undefined value. /// - public void WriteUndefined() + public virtual void WriteUndefined() { - WriteValueInternal(JavaScriptConvert.Undefined, JsonToken.Undefined); + AutoComplete(JsonToken.Undefined); } /// /// Writes raw JavaScript manually. /// /// The raw JavaScript to write. - public void WriteRaw(string javaScript) + public virtual void WriteRaw(string javaScript) { // hack. some 'raw' or 'other' token perhaps? - WriteValueInternal(javaScript, JsonToken.Undefined); + AutoComplete(JsonToken.Undefined); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(string value) + public virtual void WriteValue(string value) { - WriteValueInternal(JavaScriptConvert.ToString(value, _quoteChar), JsonToken.String); + AutoComplete(JsonToken.String); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(int value) + public virtual void WriteValue(int value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(uint value) + public virtual void WriteValue(uint value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(long value) + public virtual void WriteValue(long value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(ulong value) + public virtual void WriteValue(ulong value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(float value) + public virtual void WriteValue(float value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + AutoComplete(JsonToken.Float); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(double value) + public virtual void WriteValue(double value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + AutoComplete(JsonToken.Float); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(bool value) + public virtual void WriteValue(bool value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Boolean); + AutoComplete(JsonToken.Boolean); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(short value) + public virtual void WriteValue(short value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(ushort value) + public virtual void WriteValue(ushort value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(char value) + public virtual void WriteValue(char value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.String); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(byte value) + public virtual void WriteValue(byte value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(sbyte value) + public virtual void WriteValue(sbyte value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Integer); + AutoComplete(JsonToken.Integer); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(decimal value) + public virtual void WriteValue(decimal value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Float); + AutoComplete(JsonToken.Float); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(DateTime value) + public virtual void WriteValue(DateTime value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date); + AutoComplete(JsonToken.Date); } /// /// Writes a value. /// /// The value to write. - public void WriteValue(DateTimeOffset value) + public virtual void WriteValue(DateTimeOffset value) { - WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date); + AutoComplete(JsonToken.Date); } #endregion @@ -739,30 +711,25 @@ namespace Newtonsoft.Json /// Writes out a comment /*...*/ containing the specified text. /// /// Text to place inside the comment. - public void WriteComment(string text) + public virtual void WriteComment(string text) { AutoComplete(JsonToken.Comment); - - _writer.Write("/*"); - _writer.Write(text); - _writer.Write("*/"); } /// /// Writes out the given white space. /// /// The string of white space characters. - public void WriteWhitespace(string ws) + public virtual void WriteWhitespace(string ws) { if (ws != null) { if (!StringUtils.IsWhiteSpace(ws)) throw new JsonWriterException("Only white space characters should be used."); - - _writer.Write(ws); } } + void IDisposable.Dispose() { Dispose(true); diff --git a/Src/Newtonsoft.Json/Linq/Extensions.cs b/Src/Newtonsoft.Json/Linq/Extensions.cs index 0c7e7482..09e7ff2e 100644 --- a/Src/Newtonsoft.Json/Linq/Extensions.cs +++ b/Src/Newtonsoft.Json/Linq/Extensions.cs @@ -71,25 +71,75 @@ namespace Newtonsoft.Json.Linq return source.SelectMany(d => d.Properties()); } - public static IEnumerable PropertyValues(this IEnumerable source, string name) + public static IEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key); + } + + public static IEnumerable Values(this IEnumerable source) + { + return Values(source, null); + } + + public static IEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key); + } + + public static IEnumerable Values(this IEnumerable source) + { + return Values(source, null); + } + + //public static IEnumerable Values(this IEnumerable source, object key) + //{ + // return Values(source, null); + //} + + internal static IEnumerable Values(this IEnumerable source, object key) where T : JToken { ValidationUtils.ArgumentNotNull(source, "source"); - return source.SelectMany(d => d.Properties()) - .Where(p => string.Compare(p.Name, name, StringComparison.Ordinal) == 0) - .Select(p => p.Value) - .Convert(); + foreach (JToken token in source) + { + if (key == null) + { + foreach (JToken t in token.Children()) + { + yield return t.Convert(); ; + } + } + else + { + JToken value = token[key]; + if (value != null) + yield return value.Convert(); + } + } + + yield break; + + //IEnumerable allChildren = source.SelectMany(d => d.Children()); + //IEnumerable childrenValues = (key != null) ? allChildren.Select(p => p[key]) : allChildren.SelectMany(p => p.Children()); + + //List s = childrenValues.Convert().ToList(); + //return childrenValues.Convert(); + + // return source.SelectMany(d => d.Properties()) + //.Where(p => string.Compare(p.Name, name, StringComparison.Ordinal) == 0) + //.Select(p => p.Value) + //.Convert(); } //TODO //public static IEnumerable InDocumentOrder(this IEnumerable source) where T : JObject; - public static IEnumerable Children(this IEnumerable source) where T : JContainer - { - ValidationUtils.ArgumentNotNull(source, "source"); + //public static IEnumerable Children(this IEnumerable source) where T : JToken + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); - return source.SelectMany(c => c.Children()); - } + // return source.SelectMany(c => c.Children()); + //} public static IEnumerable Children(this IEnumerable source) { @@ -126,6 +176,9 @@ namespace Newtonsoft.Json.Linq } else { + if (token == null) + return default(U); + JValue value = token as JValue; if (value == null) throw new InvalidCastException(string.Format("Cannot cast {0} to {1}.", token.GetType(), typeof(T))); diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 34800436..aa679eb5 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -39,7 +39,12 @@ namespace Newtonsoft.Json.Linq get { return JsonTokenType.Array; } } - public JArray(params object[] content) : this((object)content) + public JArray() + { + } + + public JArray(params object[] content) + : this((object)content) { } @@ -78,7 +83,7 @@ namespace Newtonsoft.Json.Linq public static JArray Parse(string json) { - JsonReader jsonReader = new JsonReader(new StringReader(json)); + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); return Load(jsonReader); } @@ -91,16 +96,39 @@ namespace Newtonsoft.Json.Linq throw new ArgumentException(string.Format("An item of type {0} cannot be added to content.", o.Type)); } - public override void WriteTo(JsonWriter writer) + public static JArray FromObject(object o) + { + JToken token = FromObjectInternal(o); + + if (token.Type != JsonTokenType.Array) + throw new ArgumentException(string.Format("Object serialized to {0}. JArray instance expected.", token.Type)); + + return (JArray)token; + } + + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) { writer.WriteStartArray(); foreach (JToken token in Children()) { - token.WriteTo(writer); + token.WriteTo(writer, converters); } writer.WriteEndArray(); } + + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException(string.Format("Accessed JArray values with invalid key value: {0}. Array position index expected.", MiscellaneousUtils.ToString(key))); + + return GetIndex(this, (int)key); + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index 7ab72d87..561ea255 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -77,16 +77,29 @@ namespace Newtonsoft.Json.Linq } } - public override void WriteTo(JsonWriter writer) + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) { writer.WriteStartConstructor(_name); foreach (JToken token in Children()) { - token.WriteTo(writer); + token.WriteTo(writer, converters); } writer.WriteEndConstructor(); } + + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException(string.Format("Accessed JConstructor values with invalid key value: {0}. Argument position index expected.", MiscellaneousUtils.ToString(key))); + + return GetIndex(this, (int)key); + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 11c1b3f1..d6319c64 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -42,7 +42,7 @@ namespace Newtonsoft.Json.Linq set { _content = value; } } - public JToken First + public override JToken First { get { @@ -53,12 +53,17 @@ namespace Newtonsoft.Json.Linq } } - public JToken Last + public override JToken Last { get { return _content; } } - public IEnumerable Children() + public override JEnumerable Children() + { + return new JEnumerable(ChildrenInternal()); + } + + private IEnumerable ChildrenInternal() { JToken first = First; JToken current = first; @@ -71,11 +76,18 @@ namespace Newtonsoft.Json.Linq } while ((current = current.Next) != first); } - public IEnumerable Children() + public override IEnumerable Children() { return Children().Convert(); } + public override T Value(object key) + { + JToken token = this[key]; + + return Extensions.Convert(token); + } + public IEnumerable Descendants() { foreach (JToken o in Children()) @@ -92,9 +104,35 @@ namespace Newtonsoft.Json.Linq } } + internal static JToken FromObjectInternal(object o) + { + ValidationUtils.ArgumentNotNull(o, "o"); + + JsonSerializer jsonSerializer = new JsonSerializer(); + + JToken token; + using (JsonTokenWriter jsonWriter = new JsonTokenWriter()) + { + jsonSerializer.Serialize(jsonWriter, o); + token = jsonWriter.Token; + } + + return token; + } + + internal static JToken GetIndex(JContainer c, object o) + { + return c.Children().ElementAt((int)o); + } + + internal bool IsMultiContent(object content) + { + return (content is IEnumerable && !(content is string)); + } + public virtual void Add(object content) { - if (content is IEnumerable && !(content is string)) + if (IsMultiContent(content)) { IEnumerable enumerable = content as IEnumerable; foreach (object c in enumerable) diff --git a/Src/Newtonsoft.Json/Linq/JEnumerable.cs b/Src/Newtonsoft.Json/Linq/JEnumerable.cs new file mode 100644 index 00000000..8e549289 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/JEnumerable.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Linq +{ + public struct JEnumerable : IEnumerable where T : JToken + { + public static readonly JEnumerable Empty = new JEnumerable(Enumerable.Empty()); + + private IEnumerable _enumerable; + + public JEnumerable(IEnumerable enumerable) + { + ValidationUtils.ArgumentNotNull(enumerable, "enumerable"); + + _enumerable = enumerable; + } + + public IEnumerator GetEnumerator() + { + return _enumerable.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerable this[object key] + { + get { return Extensions.Values(_enumerable, key); } + } + } +} diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 5bcd9079..b639a44c 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -64,24 +64,41 @@ namespace Newtonsoft.Json.Linq .SingleOrDefault(); } - public IEnumerable PropertyValues() + public JEnumerable PropertyValues() { - return Properties().Select(p => p.Value); + return new JEnumerable(Properties().Select(p => p.Value)); } - public T PropertyValue(string name) + public override void Add(object content) { - JProperty property = Property(name); + ValidationUtils.ArgumentNotNull(content, "content"); - if (property == null) - throw new Exception(string.Format("Property '{0}' does not exist in JSON object.", name)); + if (!(content is JProperty) && !IsMultiContent(content)) + throw new ArgumentException(string.Format("Error adding {0} to JObject. JObject only supports JProperty content.", content.GetType().Name)); - // HACK - return Extensions.Convert(property.Value); + base.Add(content); + } + + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + string propertyName = key as string; + if (propertyName == null) + throw new ArgumentException(string.Format("Accessed JObject values with invalid key value: {0}. Object property name expected.", MiscellaneousUtils.ToString(key))); + + JProperty property = Property(propertyName); + + return (property != null) ? property.Value : null; + } } public static JObject Load(JsonReader reader) { + ValidationUtils.ArgumentNotNull(reader, "reader"); + if (reader.TokenType == JsonToken.None) { if (!reader.Read()) @@ -105,11 +122,21 @@ namespace Newtonsoft.Json.Linq public static JObject Parse(string json) { - JsonReader jsonReader = new JsonReader(new StringReader(json)); + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); return Load(jsonReader); } + public static JObject FromObject(object o) + { + JToken token = FromObjectInternal(o); + + if (token.Type != JsonTokenType.Object) + throw new ArgumentException(string.Format("Object serialized to {0}. JObject instance expected.", token.Type)); + + return (JObject)token; + } + internal override void ValidateObject(JToken o, JToken previous) { ValidationUtils.ArgumentNotNull(o, "o"); @@ -118,13 +145,13 @@ namespace Newtonsoft.Json.Linq throw new ArgumentException(string.Format("An item of type {0} cannot be added to content.", o.Type)); } - public override void WriteTo(JsonWriter writer) + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) { writer.WriteStartObject(); foreach (JProperty property in Properties()) { - property.WriteTo(writer); + property.WriteTo(writer, converters); } writer.WriteEndObject(); diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index ed1080b5..aba52709 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -56,7 +56,7 @@ namespace Newtonsoft.Json.Linq public JProperty(string name) { - ValidationUtils.ArgumentNotNullOrEmpty(name, "name"); + ValidationUtils.ArgumentNotNull(name, "name"); _name = name; } @@ -77,10 +77,10 @@ namespace Newtonsoft.Json.Linq throw new ArgumentException(string.Format("An item of type {0} cannot be added to content.", o.Type)); } - public override void WriteTo(JsonWriter writer) + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) { writer.WritePropertyName(_name); - Value.WriteTo(writer); + Value.WriteTo(writer, converters); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 9dd5f1fb..e3669a3b 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -107,15 +107,35 @@ namespace Newtonsoft.Json.Linq yield return o; } - //public IEnumerable ObjectsAfterSelf() - //{ - // return null; - //} + public virtual JToken this[object key] + { + get { return null; } + } - //public IEnumerable ObjectsBeforeSelf() - //{ - // return null; - //} + public virtual T Value(object key) + { + return default(T); + } + + public virtual JToken First + { + get { return null; } + } + + public virtual JToken Last + { + get { return null; } + } + + public virtual JEnumerable Children() + { + return JEnumerable.Empty; + } + + public virtual IEnumerable Children() + { + return Enumerable.Empty(); + } public void Remove() { @@ -129,13 +149,18 @@ namespace Newtonsoft.Json.Linq { } - public abstract void WriteTo(JsonWriter writer); + public abstract void WriteTo(JsonWriter writer, params JsonConverter[] converters); public override string ToString() + { + return ToString(null); + } + + private string ToString(params JsonConverter[] converters) { using (StringWriter sw = new StringWriter()) { - JsonWriter jw = new JsonWriter(sw); + JsonTextWriter jw = new JsonTextWriter(sw); jw.Formatting = Formatting.Indented; WriteTo(jw); @@ -410,6 +435,5 @@ namespace Newtonsoft.Json.Linq return (ulong)v.Value; } #endregion - } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index e5cbce05..e33e20ef 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -84,12 +84,12 @@ namespace Newtonsoft.Json.Linq return JsonTokenType.Null; else if (value is string) return JsonTokenType.String; - else if (value is long || value is int || value is short - || value is ulong || value is uint || value is ushort) + else if (value is long || value is int || value is short || value is sbyte + || value is ulong || value is uint || value is ushort || value is byte) return JsonTokenType.Integer; - else if (value is double || value is float) + else if (value is double || value is float || value is decimal) return JsonTokenType.Float; - else if (value is DateTime) + else if (value is DateTime || value is DateTimeOffset) return JsonTokenType.Date; else if (value is bool) return JsonTokenType.Boolean; @@ -113,7 +113,18 @@ namespace Newtonsoft.Json.Linq return new JValue(value, JsonTokenType.String); } - public override void WriteTo(JsonWriter writer) + private static void WriteConvertableValue(JsonWriter writer, IList converters, Action _defaultWrite, object value) + { + JsonConverter matchingConverter; + + JsonSerializer.HasMatchingConverter(converters, value.GetType(), out matchingConverter); + if (matchingConverter != null) + matchingConverter.WriteJson(writer, value); + else + _defaultWrite(value); + } + + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) { switch (_valueType) { @@ -121,16 +132,25 @@ namespace Newtonsoft.Json.Linq writer.WriteComment(_value.ToString()); break; case JsonTokenType.Integer: - writer.WriteValue(Convert.ToInt64(_value)); + WriteConvertableValue(writer, converters, v => writer.WriteValue(Convert.ToInt64(v)), _value); break; case JsonTokenType.Float: - writer.WriteValue(Convert.ToDouble(_value)); + WriteConvertableValue(writer, converters, v => writer.WriteValue(Convert.ToDouble(v)), _value); break; case JsonTokenType.String: - writer.WriteValue(_value.ToString()); + WriteConvertableValue(writer, converters, v => writer.WriteValue(v.ToString()), _value); break; case JsonTokenType.Boolean: - writer.WriteValue(Convert.ToBoolean(_value)); + WriteConvertableValue(writer, converters, v => writer.WriteValue(Convert.ToBoolean(v)), _value); + break; + case JsonTokenType.Date: + WriteConvertableValue(writer, converters, v => + { + if (v is DateTimeOffset) + writer.WriteValue((DateTimeOffset)v); + else + writer.WriteValue(Convert.ToDateTime(v)); + }, _value); break; case JsonTokenType.Null: writer.WriteNull(); @@ -138,9 +158,6 @@ namespace Newtonsoft.Json.Linq case JsonTokenType.Undefined: writer.WriteUndefined(); break; - case JsonTokenType.Date: - writer.WriteValue(Convert.ToDateTime(_value)); - break; default: throw new ArgumentOutOfRangeException("TokenType", _valueType, "Unexpected token type."); } diff --git a/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs b/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs new file mode 100644 index 00000000..216a0d25 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + public class JsonTokenReader : JsonReader + { + private JToken _root; + private JToken _parent; + private JToken _current; + + public JsonTokenReader(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + _root = token; + _current = token; + } + + public override bool Read() + { + if (CurrentState != State.Start) + { + JContainer container = _current as JContainer; + if (container != null && _parent != container) + return ReadInto(container); + else + return ReadOver(_current); + } + + SetToken(_current); + return true; + } + + private bool ReadOver(JToken t) + { + if (t == _root) + return ReadToEnd(); + + JToken next = t.Next; + if ((next == null || next == t) || t == t.Parent.Last) + { + if (t.Parent == null) + return ReadToEnd(); + + return SetEnd(t.Parent); + } + else + { + _current = next; + SetToken(_current); + return true; + } + } + + private bool ReadToEnd() + { + //CurrentState = State.Finished; + return false; + } + + private JsonToken? GetEndToken(JContainer c) + { + switch (c.Type) + { + case JsonTokenType.Object: + return JsonToken.EndObject; + case JsonTokenType.Array: + return JsonToken.EndArray; + case JsonTokenType.Constructor: + return JsonToken.EndConstructor; + case JsonTokenType.Property: + return null; + default: + throw new ArgumentOutOfRangeException("Type", c.Type, "Unexpected JContainer type."); + } + } + + private bool ReadInto(JContainer c) + { + JToken firstChild = c.First; + if (firstChild == null) + { + return SetEnd(c); + } + else + { + SetToken(firstChild); + _current = firstChild; + _parent = c; + return true; + } + } + + private bool SetEnd(JContainer c) + { + JsonToken? endToken = GetEndToken(c); + if (endToken != null) + { + SetToken(endToken.Value); + _current = c; + _parent = c; + return true; + } + else + { + return ReadOver(c); + } + } + + private void SetToken(JToken token) + { + switch (token.Type) + { + case JsonTokenType.Object: + SetToken(JsonToken.StartObject); + break; + case JsonTokenType.Array: + SetToken(JsonToken.StartArray); + break; + case JsonTokenType.Constructor: + SetToken(JsonToken.StartConstructor); + break; + case JsonTokenType.Property: + SetToken(JsonToken.PropertyName, ((JProperty)token).Name); + break; + case JsonTokenType.Comment: + SetToken(JsonToken.Comment, ((JValue)token).Value); + break; + case JsonTokenType.Integer: + SetToken(JsonToken.Integer, ((JValue)token).Value); + break; + case JsonTokenType.Float: + SetToken(JsonToken.Float, ((JValue)token).Value); + break; + case JsonTokenType.String: + SetToken(JsonToken.String, ((JValue)token).Value); + break; + case JsonTokenType.Boolean: + SetToken(JsonToken.Boolean, ((JValue)token).Value); + break; + case JsonTokenType.Null: + SetToken(JsonToken.Null, ((JValue)token).Value); + break; + case JsonTokenType.Undefined: + SetToken(JsonToken.Undefined, ((JValue)token).Value); + break; + case JsonTokenType.Date: + SetToken(JsonToken.Date, ((JValue)token).Value); + break; + default: + throw new ArgumentOutOfRangeException("Type", token.Type, "Unexpected JsonTokenType."); + } + } + } +} diff --git a/Src/Newtonsoft.Json/Linq/JsonTokenType.cs b/Src/Newtonsoft.Json/Linq/JsonTokenType.cs index 332b711b..05c1d232 100644 --- a/Src/Newtonsoft.Json/Linq/JsonTokenType.cs +++ b/Src/Newtonsoft.Json/Linq/JsonTokenType.cs @@ -32,6 +32,7 @@ namespace Newtonsoft.Json.Linq { public enum JsonTokenType { + None, Object, Array, Constructor, diff --git a/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs new file mode 100644 index 00000000..265b42f8 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + public class JsonTokenWriter : JsonWriter + { + private JContainer _token; + private JContainer _parent; + + public JContainer Token + { + get { return _token; } + } + + public JsonTokenWriter(JContainer container) + { + ValidationUtils.ArgumentNotNull(container, "container"); + + _token = container; + _parent = container; + } + + public JsonTokenWriter() + { + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + AddParent(new JObject()); + } + + private void AddParent(JContainer container) + { + if (_parent == null) + _token = container; + else + _parent.Add(container); + + _parent = container; + } + + private void RemoveParent() + { + _parent = _parent.Parent; + + if (_parent != null && _parent.Type == JsonTokenType.Property) + _parent = _parent.Parent; + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + AddParent(new JArray()); + } + + public override void WriteStartConstructor(string name) + { + base.WriteStartConstructor(name); + + AddParent(new JConstructor()); + } + + protected override void WriteEnd(JsonToken token) + { + RemoveParent(); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + AddParent(new JProperty(name)); + } + + private void AddValue(object value, JsonToken token) + { + _parent.Add(new JValue(value)); + + if (_parent.Type == JsonTokenType.Property) + _parent = _parent.Parent; + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + AddValue(null, JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + AddValue(null, JsonToken.Undefined); + } + + /// + /// Writes raw JavaScript manually. + /// + /// The raw JavaScript to write. + public override void WriteRaw(string javaScript) + { + base.WriteRaw(javaScript); + // hack. some 'raw' or 'other' token perhaps? + AddValue(javaScript, JsonToken.Undefined); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + AddValue(value ?? string.Empty, JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(uint value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(ulong value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(ushort value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + AddValue(value.ToString(), JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Date); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Date); + } + #endregion + } +} diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 530039ee..5ad7487f 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -78,15 +78,13 @@ - - + + - - @@ -96,8 +94,11 @@ + + + diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs index 00d81359..f4fbdc37 100644 --- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -78,7 +78,10 @@ namespace Newtonsoft.Json.Utilities public static string ToString(object value) { - return (value != null) ? value.ToString() : "{null}"; + if (value == null) + return "{null}"; + + return (value is string) ? @"""" + value.ToString() + @"""" : value.ToString(); } } }