-JsonTextReader, BsonReader performance

-Fix dictionary key encoding when serializing
-Fix WrappedDictionary being set to a property when deserializing
-Fix Silverlight serialization security error
This commit is contained in:
JamesNK
2010-01-01 23:13:35 +00:00
parent 974178a162
commit 324c4e4bc5
25 changed files with 971 additions and 566 deletions
@@ -97,7 +97,11 @@ namespace Newtonsoft.Json.Tests.Converters
[Test]
public void SerializeCustomFormattedDateTime()
{
IsoDateTimeConverter converter = new IsoDateTimeConverter() { DateTimeFormat = "dd/MM/yyyy" };
IsoDateTimeConverter converter = new IsoDateTimeConverter
{
DateTimeFormat = "dd/MM/yyyy",
Culture = CultureInfo.InvariantCulture
};
string json = @"""09/12/2006""";
@@ -152,6 +152,9 @@ now brown cow?", '"', true);
result = JavaScriptUtils.ToEscapedJavaScriptString(@"Fred's ""cat"".", '\'', true);
Assert.AreEqual(result, @"'Fred\'s ""cat"".'");
result = JavaScriptUtils.ToEscapedJavaScriptString("\u001farray\u003caddress");
Assert.AreEqual(result, @"""\u001farray<address""");
}
[Test]
+139 -1
View File
@@ -48,7 +48,7 @@ namespace Newtonsoft.Json.Tests
,{""t"":""CSCO"", ""n"":""Cisco Systems, Inc."", ""e"":""NASDAQ"", ""id"":""99624""}
,{""t"":""CVX"", ""n"":""Chevron Corporation"", ""e"":""NYSE"", ""id"":""667226""}
,{""t"":""TM"", ""n"":""Toyota Motor Corporation (ADR)"", ""e"":""NYSE"", ""id"":""655880""}
,{""t"":""JPM"", ""n"":""JPMorgan Chase \x26 Co."", ""e"":""NYSE"", ""id"":""665639""}
,{""t"":""JPM"", ""n"":""JPMorgan Chase \\x26 Co."", ""e"":""NYSE"", ""id"":""665639""}
,{""t"":""COP"", ""n"":""ConocoPhillips"", ""e"":""NYSE"", ""id"":""1691168""}
,{""t"":""LFC"", ""n"":""China Life Insurance Company Ltd. (ADR)"", ""e"":""NYSE"", ""id"":""688679""}
,{""t"":""NOK"", ""n"":""Nokia Corporation (ADR)"", ""e"":""NYSE"", ""id"":""657729""}
@@ -138,6 +138,8 @@ namespace Newtonsoft.Json.Tests
Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject);
Assert.AreEqual(7, jsonReader.LineNumber);
Assert.AreEqual(1, jsonReader.LinePosition);
Assert.IsFalse(jsonReader.Read());
}
}
@@ -176,10 +178,12 @@ namespace Newtonsoft.Json.Tests
jsonReader.Read();
Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer);
Assert.AreEqual(1, jsonReader.Value);
Assert.AreEqual(3, jsonReader.Depth);
jsonReader.Read();
Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer);
Assert.AreEqual(2, jsonReader.Value);
Assert.AreEqual(3, jsonReader.Depth);
jsonReader.Read();
@@ -192,6 +196,140 @@ namespace Newtonsoft.Json.Tests
}
}
[Test]
[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null.
Parameter name: reader")]
public void NullTextReader()
{
new JsonTextReader(null);
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")]
public void UnexpectedEndOfString()
{
JsonReader reader = new JsonTextReader(new StringReader("'hi"));
reader.Read();
}
[Test]
public void ReadNullTerminatorStrings()
{
JsonReader reader = new JsonTextReader(new StringReader("'h\0i'"));
Assert.IsTrue(reader.Read());
Assert.AreEqual("h\0i", reader.Value);
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end while parsing unicode character. Line 1, position 7.")]
public void UnexpectedEndOfHex()
{
JsonReader reader = new JsonTextReader(new StringReader(@"'h\u006"));
reader.Read();
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")]
public void UnexpectedEndOfControlCharacter()
{
JsonReader reader = new JsonTextReader(new StringReader(@"'h\"));
reader.Read();
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected character encountered while parsing value: t. Line 1, position 1.")]
public void ReadBytesWithBadCharacter()
{
JsonReader reader = new JsonTextReader(new StringReader(@"true"));
reader.ReadAsBytes();
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 17.")]
public void ReadBytesWithUnexpectedEnd()
{
string helloWorld = "Hello world!";
byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld);
JsonReader reader = new JsonTextReader(new StringReader(@"'" + Convert.ToBase64String(helloWorldData)));
reader.ReadAsBytes();
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end when reading bytes: Line 1, position 3.")]
public void ReadBytesNoStartWithUnexpectedEnd()
{
JsonReader reader = new JsonTextReader(new StringReader(@"[ "));
Assert.IsTrue(reader.Read());
reader.ReadAsBytes();
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end when parsing unquoted property name. Line 1, position 4.")]
public void UnexpectedEndWhenParsingUnquotedProperty()
{
JsonReader reader = new JsonTextReader(new StringReader(@"{aww"));
Assert.IsTrue(reader.Read());
reader.Read();
}
[Test]
public void ParsingQuotedPropertyWithControlCharacters()
{
JsonReader reader = new JsonTextReader(new StringReader(@"{'hi\r\nbye':1}"));
Assert.IsTrue(reader.Read());
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.PropertyName, reader.TokenType);
Assert.AreEqual(@"hi
bye", reader.Value);
Assert.IsTrue(reader.Read());
Assert.IsTrue(reader.Read());
Assert.IsFalse(reader.Read());
}
[Test]
public void ReadBytesFollowingNumberInArray()
{
string helloWorld = "Hello world!";
byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld);
JsonReader reader = new JsonTextReader(new StringReader(@"[1,'" + Convert.ToBase64String(helloWorldData) + @"']"));
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.StartArray, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
byte[] data = reader.ReadAsBytes();
Assert.AreEqual(helloWorldData, data);
Assert.AreEqual(JsonToken.Bytes, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndArray, reader.TokenType);
Assert.IsFalse(reader.Read());
}
[Test]
public void ReadBytesFollowingNumberInObject()
{
string helloWorld = "Hello world!";
byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld);
JsonReader reader = new JsonTextReader(new StringReader(@"{num:1,data:'" + Convert.ToBase64String(helloWorldData) + @"'}"));
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.StartObject, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
Assert.IsTrue(reader.Read());
byte[] data = reader.ReadAsBytes();
Assert.AreEqual(helloWorldData, data);
Assert.AreEqual(JsonToken.Bytes, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndObject, reader.TokenType);
Assert.IsFalse(reader.Read());
}
[Test]
public void ReadingEscapedStrings()
{
@@ -527,7 +527,7 @@ namespace Newtonsoft.Json.Tests
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Null, reader.TokenType);
Assert.AreEqual(@"Value null is not defined in enum. Line 1, position 6.", validationEventArgs.Message);
Assert.AreEqual(@"Value null is not defined in enum. Line 1, position 5.", validationEventArgs.Message);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndArray, reader.TokenType);
@@ -564,7 +564,7 @@ namespace Newtonsoft.Json.Tests
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Boolean, reader.TokenType);
Assert.AreEqual(@"Value false is not defined in enum. Line 1, position 12.", validationEventArgs.Message);
Assert.AreEqual(@"Value false is not defined in enum. Line 1, position 11.", validationEventArgs.Message);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndArray, reader.TokenType);
@@ -1005,7 +1005,7 @@ namespace Newtonsoft.Json.Tests
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Null, reader.TokenType);
Assert.AreEqual("Index 3 has not been defined and the schema does not allow additional items. Line 1, position 14.", validationEventArgs.Message);
Assert.AreEqual("Index 3 has not been defined and the schema does not allow additional items. Line 1, position 13.", validationEventArgs.Message);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndArray, reader.TokenType);
File diff suppressed because one or more lines are too long
@@ -27,6 +27,7 @@ using System;
using System.Collections.Generic;
#if !SILVERLIGHT && !PocketPC && !NET20
using System.ComponentModel.DataAnnotations;
using System.Web.Script.Serialization;
#endif
using System.Text;
using NUnit.Framework;
@@ -131,12 +132,21 @@ namespace Newtonsoft.Json.Tests.Serialization
[Test]
public void DeserializeJavaScriptDate()
{
DateTime dateValue = new DateTime(2000, 3, 30);
DateTime dateValue = new DateTime(2010, 3, 30);
Dictionary<string, object> testDictionary = new Dictionary<string, object>();
testDictionary["date"] = dateValue;
string jsonText = JsonConvert.SerializeObject(testDictionary);
#if !PocketPC && !NET20
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary<string, object>));
serializer.WriteObject(ms, testDictionary);
byte[] data = ms.ToArray();
string output = Encoding.UTF8.GetString(data, 0, data.Length);
#endif
Dictionary<string, object> deserializedDictionary = (Dictionary<string, object>)JsonConvert.DeserializeObject(jsonText, typeof(Dictionary<string, object>));
DateTime deserializedDate = (DateTime)deserializedDictionary["date"];
@@ -403,6 +413,37 @@ keyword such as type of business.""
Assert.AreEqual(dataContractResult[0], jsonNetResult[0]);
}
[Test]
public void BackslashEqivilence()
{
string json = @"[""vvv\/vvv\tvvv\""vvv\bvvv\nvvv\rvvv\\vvv\fvvv""]";
#if !SILVERLIGHT
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
List<string> javaScriptSerializerResult = javaScriptSerializer.Deserialize<List<string>>(json);
#endif
DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(List<string>));
List<string> dataContractResult = (List<string>)s.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json)));
List<string> jsonNetResult = JsonConvert.DeserializeObject<List<string>>(json);
Assert.AreEqual(1, jsonNetResult.Count);
Assert.AreEqual(dataContractResult[0], jsonNetResult[0]);
#if !SILVERLIGHT
Assert.AreEqual(javaScriptSerializerResult[0], jsonNetResult[0]);
#endif
}
[Test]
[ExpectedException(typeof(JsonReaderException), ExpectedMessage = @"Bad JSON escape sequence: \j. Line 1, position 7.")]
public void InvalidBackslash()
{
string json = @"[""vvv\jvvv""]";
JsonConvert.DeserializeObject<List<string>>(json);
}
[Test]
public void DateTimeTest()
{
@@ -1068,9 +1109,6 @@ keyword such as type of business.""
]
}";
//JavaScriptSerializer serializer = new JavaScriptSerializer();
//GoogleMapGeocoderStructure jsonGoogleMapGeocoder = serializer.Deserialize<GoogleMapGeocoderStructure>(json);
GoogleMapGeocoderStructure jsonGoogleMapGeocoder = JsonConvert.DeserializeObject<GoogleMapGeocoderStructure>(json);
}
@@ -2051,5 +2089,39 @@ keyword such as type of business.""
PersonPropertyClass newPersonPropertyClass = JsonConvert.DeserializeObject<PersonPropertyClass>(json);
Assert.AreEqual(wagePerson.HourlyWage, ((WagePerson) newPersonPropertyClass.Person).HourlyWage);
}
public class ExistingValueClass
{
public Dictionary<string, string> Dictionary { get; set; }
public List<string> List { get; set; }
public ExistingValueClass()
{
Dictionary = new Dictionary<string, string>
{
{"existing", "yup"}
};
List = new List<string>
{
"existing"
};
}
}
[Test]
public void DeserializePopulateDictionaryAndList()
{
ExistingValueClass d = JsonConvert.DeserializeObject<ExistingValueClass>(@"{'Dictionary':{appended:'appended',existing:'new'}}");
Assert.IsNotNull(d);
Assert.IsNotNull(d.Dictionary);
Assert.AreEqual(typeof(Dictionary<string, string>), d.Dictionary.GetType());
Assert.AreEqual(typeof(List<string>), d.List.GetType());
Assert.AreEqual(2, d.Dictionary.Count);
Assert.AreEqual("new", d.Dictionary["existing"]);
Assert.AreEqual("appended", d.Dictionary["appended"]);
Assert.AreEqual(1, d.List.Count);
Assert.AreEqual("existing", d.List[0]);
}
}
}
+28 -32
View File
@@ -46,6 +46,8 @@ namespace Newtonsoft.Json.Bson
private char[] _charBuffer;
private BsonType _currentElementType;
private BsonReaderState _bsonReaderState;
private ContainerContext _currentContext;
private const int MaxCharBytesSize = 128;
private enum BsonReaderState
@@ -63,20 +65,9 @@ namespace Newtonsoft.Json.Bson
private class ContainerContext
{
public JTokenType Type { get; private set; }
public int Length { get; set; }
private int _position;
public int Position
{
get { return _position; }
set
{
if (value > Length && Length != 0)
throw new JsonReaderException("Read past end of current container context.");
_position = value;
}
}
public readonly JTokenType Type;
public int Length;
public int Position;
public ContainerContext(JTokenType type)
{
@@ -188,7 +179,7 @@ namespace Newtonsoft.Json.Bson
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
ContainerContext newContext = new ContainerContext(JTokenType.Object);
_stack.Add(newContext);
PushContext(newContext);
newContext.Length = ReadInt32();
return true;
@@ -270,7 +261,7 @@ namespace Newtonsoft.Json.Bson
SetToken(token);
ContainerContext newContext = new ContainerContext(type);
_stack.Add(newContext);
PushContext(newContext);
newContext.Length = ReadInt32();
return true;
}
@@ -292,7 +283,7 @@ namespace Newtonsoft.Json.Bson
ReadType(_currentElementType);
return true;
case State.PostValue:
ContainerContext context = GetCurrentContext();
ContainerContext context = _currentContext;
if (context == null)
return false;
@@ -317,8 +308,8 @@ namespace Newtonsoft.Json.Bson
if (ReadByte() != 0)
throw new JsonReaderException("Unexpected end of object byte value.");
_stack.RemoveAt(_stack.Count - 1);
if (_stack.Count != 0)
PopContext();
if (_currentContext != null)
MovePosition(context.Length);
JsonToken endToken = (context.Type == JTokenType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
@@ -344,6 +335,21 @@ namespace Newtonsoft.Json.Bson
return false;
}
private void PopContext()
{
_stack.RemoveAt(_stack.Count - 1);
if (_stack.Count == 0)
_currentContext = null;
else
_currentContext = _stack[_stack.Count - 1];
}
private void PushContext(ContainerContext newContext)
{
_stack.Add(newContext);
_currentContext = newContext;
}
private byte ReadByte()
{
MovePosition(1);
@@ -366,7 +372,7 @@ namespace Newtonsoft.Json.Bson
SetToken(JsonToken.StartObject);
ContainerContext newContext = new ContainerContext(JTokenType.Object);
_stack.Add(newContext);
PushContext(newContext);
newContext.Length = ReadInt32();
break;
}
@@ -375,7 +381,7 @@ namespace Newtonsoft.Json.Bson
SetToken(JsonToken.StartArray);
ContainerContext newContext = new ContainerContext(JTokenType.Array);
_stack.Add(newContext);
PushContext(newContext);
newContext.Length = ReadInt32();
break;
}
@@ -561,19 +567,9 @@ namespace Newtonsoft.Json.Bson
return (BsonType)_reader.ReadSByte();
}
private ContainerContext GetCurrentContext()
{
int count = _stack.Count;
if (count == 0)
return null;
return _stack[count - 1];
}
private void MovePosition(int count)
{
ContainerContext context = GetCurrentContext();
context.Position += count;
_currentContext.Position += count;
}
private byte[] ReadBytes(int count)
+81 -20
View File
@@ -29,6 +29,7 @@ using System.Globalization;
using Newtonsoft.Json.Utilities;
using System.Xml;
using Newtonsoft.Json.Converters;
using System.Text;
namespace Newtonsoft.Json
{
@@ -96,14 +97,11 @@ namespace Newtonsoft.Json
/// <returns>A JSON string representation of the <see cref="DateTime"/>.</returns>
public static string ToString(DateTime value)
{
TimeSpan utcOffset;
#if !PocketPC && !NET20
utcOffset = TimeZoneInfo.Local.GetUtcOffset(value);
#else
utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(value);
#endif
return ToStringInternal(value, utcOffset, value.Kind);
using (StringWriter writer = StringUtils.CreateStringWriter(64))
{
WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind);
return writer.ToString();
}
}
#if !PocketPC && !NET20
@@ -114,33 +112,95 @@ namespace Newtonsoft.Json
/// <returns>A JSON string representation of the <see cref="DateTimeOffset"/>.</returns>
public static string ToString(DateTimeOffset value)
{
return ToStringInternal(value.UtcDateTime, value.Offset, DateTimeKind.Local);
using (StringWriter writer = StringUtils.CreateStringWriter(64))
{
WriteDateTimeString(writer, value.UtcDateTime, value.Offset, DateTimeKind.Local);
return writer.ToString();
}
}
#endif
internal static string ToStringInternal(DateTime value, TimeSpan offset, DateTimeKind kind)
private static TimeSpan GetUtcOffset(DateTime dateTime)
{
long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value);
#if SILVERLIGHT
return TimeZoneInfo.Local.GetUtcOffset(dateTime);
#else
return TimeZone.CurrentTimeZone.GetUtcOffset(dateTime);
#endif
}
string timezoneOffset;
internal static void WriteDateTimeString(TextWriter writer, DateTime value)
{
WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind);
}
internal static void WriteDateTimeString(TextWriter writer, DateTime value, TimeSpan offset, DateTimeKind kind)
{
long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value, offset);
writer.Write(@"""\/Date(");
writer.Write(javaScriptTicks);
switch (kind)
{
case DateTimeKind.Local:
case DateTimeKind.Unspecified:
timezoneOffset = offset.Hours.ToString("+00;-00", CultureInfo.InvariantCulture) + offset.Minutes.ToString("00;00", CultureInfo.InvariantCulture);
break;
default:
timezoneOffset = string.Empty;
writer.Write((offset.Ticks >= 0L) ? "+" : "-");
int absHours = Math.Abs(offset.Hours);
if (absHours < 10)
writer.Write(0);
writer.Write(absHours);
int absMinutes = Math.Abs(offset.Minutes);
if (absMinutes < 10)
writer.Write(0);
writer.Write(absMinutes);
break;
}
return @"""\/Date(" + javaScriptTicks.ToString(CultureInfo.InvariantCulture) + timezoneOffset + @")\/""";
writer.Write(@")\/""");
}
private static long ToUniversalTicks(DateTime dateTime)
{
if (dateTime.Kind == DateTimeKind.Utc)
return dateTime.Ticks;
return ToUniversalTicks(dateTime, GetUtcOffset(dateTime));
}
private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset)
{
if (dateTime.Kind == DateTimeKind.Utc)
return dateTime.Ticks;
long ticks = dateTime.Ticks - offset.Ticks;
if (ticks > 3155378975999999999L)
return 3155378975999999999L;
if (ticks < 0L)
return 0L;
return ticks;
}
internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, TimeSpan offset)
{
long universialTicks = ToUniversalTicks(dateTime, offset);
return UniversialTicksToJavaScriptTicks(universialTicks);
}
internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime)
{
DateTime utcDateTime = dateTime.ToUniversalTime();
long universialTicks = ToUniversalTicks(dateTime);
long javaScriptTicks = (utcDateTime.Ticks - InitialJavaScriptDateTicks) / 10000;
return UniversialTicksToJavaScriptTicks(universialTicks);
}
private static long UniversialTicksToJavaScriptTicks(long universialTicks)
{
long javaScriptTicks = (universialTicks - InitialJavaScriptDateTicks) / 10000;
return javaScriptTicks;
}
@@ -524,7 +584,8 @@ namespace Newtonsoft.Json
{
JsonSerializer jsonSerializer = JsonSerializer.Create(settings);
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
StringBuilder sb = new StringBuilder(128);
StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture);
using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
{
jsonWriter.Formatting = formatting;
+4 -1
View File
@@ -102,6 +102,7 @@ namespace Newtonsoft.Json
private Type _valueType;
private char _quoteChar;
private State _currentState;
private JTokenType _currentTypeContext;
/// <summary>
/// Gets the current reader state.
@@ -182,6 +183,7 @@ namespace Newtonsoft.Json
{
_stack.Add(value);
_top++;
_currentTypeContext = value;
}
private JTokenType Pop()
@@ -189,13 +191,14 @@ namespace Newtonsoft.Json
JTokenType value = Peek();
_stack.RemoveAt(_stack.Count - 1);
_top--;
_currentTypeContext = _stack[_top - 1];
return value;
}
private JTokenType Peek()
{
return _stack[_top - 1];
return _currentTypeContext;
}
/// <summary>
-10
View File
@@ -418,16 +418,6 @@ namespace Newtonsoft.Json
serializerWriter.Serialize(jsonWriter, value);
}
internal bool HasClassConverter(JsonContract contract, out JsonConverter converter)
{
if (contract == null)
throw new ArgumentNullException("contract");
converter = contract.Converter;
return (converter != null);
}
internal bool HasMatchingConverter(Type type, out JsonConverter matchingConverter)
{
if (HasMatchingConverter(_converters, type, out matchingConverter))
+238 -254
View File
@@ -40,10 +40,10 @@ namespace Newtonsoft.Json
{
private readonly TextReader _reader;
private readonly StringBuffer _buffer;
private char _currentChar;
private char? _lastChar;
private int _currentLinePosition;
private int _currentLineNumber;
private bool _currentCharCarriageReturn;
private bool _end;
/// <summary>
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
@@ -61,7 +61,7 @@ namespace Newtonsoft.Json
private void ParseString(char quote)
{
ReadStringIntoBuffer(quote, null);
ReadStringIntoBuffer(quote);
string text = _buffer.ToString();
_buffer.Position = 0;
@@ -77,23 +77,24 @@ namespace Newtonsoft.Json
}
}
private void ReadStringIntoBuffer(char quote, int? untilPosition)
private void ReadStringIntoBuffer(char quote)
{
bool stringTerminated = false;
bool hexNumber = false;
int hexCount = 0;
while (!stringTerminated && (untilPosition == null || _buffer.Position < untilPosition) && MoveNext())
while (true)
{
if (hexNumber)
hexCount++;
char currentChar = MoveNext();
switch (_currentChar)
switch (currentChar)
{
case '\0':
if (_end)
throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition);
_buffer.Append('\0');
break;
case '\\':
if (MoveNext())
if ((currentChar = MoveNext()) != '\0' || !_end)
{
switch (_currentChar)
switch (currentChar)
{
case 'b':
_buffer.Append('\b');
@@ -110,13 +111,29 @@ namespace Newtonsoft.Json
case 'r':
_buffer.Append('\r');
break;
case '\\':
_buffer.Append('\\');
break;
case '"':
case '\'':
case '/':
_buffer.Append(currentChar);
break;
case 'u':
// note the start of a hex character
hexNumber = true;
char[] hexValues = new char[4];
for (int i = 0; i < hexValues.Length; i++)
{
if ((currentChar = MoveNext()) != '\0' || !_end)
hexValues[i] = currentChar;
else
throw CreateJsonReaderException("Unexpected end while parsing unicode character. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
}
char hexChar = Convert.ToChar(int.Parse(new string(hexValues), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo));
_buffer.Append(hexChar);
break;
default:
_buffer.Append(_currentChar);
break;
throw CreateJsonReaderException("Bad JSON escape sequence: {0}. Line {1}, position {2}.", @"\" + currentChar, _currentLineNumber, _currentLinePosition);
}
}
else
@@ -126,34 +143,20 @@ namespace Newtonsoft.Json
break;
case '"':
case '\'':
if (_currentChar == quote)
stringTerminated = true;
if (currentChar == quote)
{
return;
}
else
goto default;
{
_buffer.Append(currentChar);
}
break;
default:
_buffer.Append(_currentChar);
_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 CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition);
ClearCurrentChar();
}
private JsonReaderException CreateJsonReaderException(string format, params object[] args)
@@ -162,41 +165,6 @@ namespace Newtonsoft.Json
return new JsonReaderException(message, null, _currentLineNumber, _currentLinePosition);
}
/// <summary>
/// Sets the current token and value.
/// </summary>
/// <param name="newToken">The new token.</param>
/// <param name="value">The value.</param>
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);
@@ -236,34 +204,32 @@ namespace Newtonsoft.Json
private const int LineFeedValue = StringUtils.LineFeed;
private const int CarriageReturnValue = StringUtils.CarriageReturn;
private bool MoveNext()
private char MoveNext()
{
int value = _reader.Read();
switch (value)
{
case -1:
return false;
_end = true;
return '\0';
case CarriageReturnValue:
if (_reader.Peek() == LineFeedValue)
_reader.Read();
_currentLineNumber++;
_currentLinePosition = 0;
_currentCharCarriageReturn = true;
break;
case LineFeedValue:
if (!_currentCharCarriageReturn)
_currentLineNumber++;
_currentLineNumber++;
_currentLinePosition = 0;
_currentCharCarriageReturn = false;
break;
default:
_currentLinePosition++;
_currentCharCarriageReturn = false;
break;
}
_currentChar = (char)value;
return true;
return (char)value;
}
private bool HasNext()
@@ -271,14 +237,9 @@ namespace Newtonsoft.Json
return (_reader.Peek() != -1);
}
private char PeekNext()
private int PeekNext()
{
return (char)_reader.Peek();
}
private void ClearCurrentChar()
{
_currentChar = '\0';
return _reader.Peek();
}
/// <summary>
@@ -291,11 +252,19 @@ namespace Newtonsoft.Json
{
while (true)
{
if (_currentChar == '\0')
char currentChar;
if (_lastChar != null)
{
if (!MoveNext())
return false;
currentChar = _lastChar.Value;
_lastChar = null;
}
else
{
currentChar = MoveNext();
}
if (currentChar == '\0' && _end)
return false;
switch (CurrentState)
{
@@ -305,16 +274,16 @@ namespace Newtonsoft.Json
case State.ArrayStart:
case State.Constructor:
case State.ConstructorStart:
return ParseValue();
return ParseValue(currentChar);
case State.Complete:
break;
case State.Object:
case State.ObjectStart:
return ParseObject();
return ParseObject(currentChar);
case State.PostValue:
// returns true if it hits
// end of object or array
if (ParsePostValue())
if (ParsePostValue(currentChar))
return true;
break;
case State.Closed:
@@ -337,14 +306,23 @@ namespace Newtonsoft.Json
{
while (true)
{
if (_currentChar == '\0')
char currentChar;
if (_lastChar != null)
{
if (!MoveNext())
throw CreateJsonReaderException("Unexpected end: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
currentChar = _lastChar.Value;
_lastChar = null;
}
else
{
currentChar = MoveNext();
}
if (currentChar == '\0' && _end)
throw CreateJsonReaderException("Unexpected end when reading bytes: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
switch (CurrentState)
{
case State.PostValue:
case State.Start:
case State.Property:
case State.Array:
@@ -353,11 +331,11 @@ namespace Newtonsoft.Json
case State.ConstructorStart:
do
{
switch (_currentChar)
switch (currentChar)
{
case '"':
case '\'':
ReadStringIntoBuffer(_currentChar, null);
ReadStringIntoBuffer(currentChar);
byte[] data = Convert.FromBase64CharArray(_buffer.GetInternalBuffer(), 0, _buffer.Position);
_buffer.Position = 0;
@@ -368,19 +346,27 @@ namespace Newtonsoft.Json
case 'n':
ParseNull();
return null;
case ' ':
case StringUtils.Tab:
case StringUtils.LineFeed:
case StringUtils.CarriageReturn:
// eat
break;
case ',':
SetStateBasedOnCurrent();
break;
default:
if (char.IsWhiteSpace(_currentChar))
if (char.IsWhiteSpace(currentChar))
{
// eat
}
else
{
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
break;
}
} while (MoveNext());
ParseValue();
} while ((currentChar = MoveNext()) != '\0' || !_end);
break;
default:
throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition);
@@ -388,115 +374,114 @@ namespace Newtonsoft.Json
}
}
private bool ParsePostValue()
private bool ParsePostValue(char currentChar)
{
do
{
switch (_currentChar)
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
// finished parsing
SetStateBasedOnCurrent();
ClearCurrentChar();
return false;
case ' ':
// eat whitespace
ClearCurrentChar();
break;
default:
if (char.IsWhiteSpace(_currentChar))
{
// eat whitespace
ClearCurrentChar();
}
else
{
throw CreateJsonReaderException("After parsing a value an unexpected character was encoutered: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
}
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;
case ' ':
case StringUtils.Tab:
case StringUtils.LineFeed:
case StringUtils.CarriageReturn:
// eat
break;
default:
if (char.IsWhiteSpace(_currentChar))
if (char.IsWhiteSpace(currentChar))
{
// eat
}
else
{
return ParseProperty();
throw CreateJsonReaderException("After parsing a value an unexpected character was encoutered: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
break;
}
} while (MoveNext());
} while ((currentChar = MoveNext()) != '\0' || !_end);
return false;
}
private bool ParseProperty()
private bool ParseObject(char currentChar)
{
do
{
switch (currentChar)
{
case '}':
SetToken(JsonToken.EndObject);
return true;
case '/':
ParseComment();
return true;
case ' ':
case StringUtils.Tab:
case StringUtils.LineFeed:
case StringUtils.CarriageReturn:
// eat
break;
default:
if (char.IsWhiteSpace(currentChar))
{
// eat
}
else
{
return ParseProperty(currentChar);
}
break;
}
} while ((currentChar = MoveNext()) != '\0' || !_end);
return false;
}
private bool ParseProperty(char firstChar)
{
char currentChar = firstChar;
char quoteChar;
if (ValidIdentifierChar(_currentChar))
if (ValidIdentifierChar(currentChar))
{
quoteChar = '\0';
ParseUnquotedProperty();
currentChar = ParseUnquotedProperty(currentChar);
}
else if (_currentChar == '"' || _currentChar == '\'')
else if (currentChar == '"' || currentChar == '\'')
{
quoteChar = _currentChar;
ParseQuotedProperty(_currentChar);
quoteChar = currentChar;
ReadStringIntoBuffer(quoteChar);
currentChar = MoveNext();
}
else
{
throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
if (_currentChar != ':')
if (currentChar != ':')
{
MoveNext();
currentChar = MoveNext();
// finished property. skip any whitespace and move to colon
EatWhitespace(false);
EatWhitespace(currentChar, false, out currentChar);
if (_currentChar != ':')
throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
if (currentChar != ':')
throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
SetToken(JsonToken.PropertyName, _buffer.ToString());
@@ -506,60 +491,46 @@ namespace Newtonsoft.Json
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 CreateJsonReaderException("Unclosed quoted property. Expected: {0}. Line {1}, position {2}.", quoteChar, _currentLineNumber, _currentLinePosition);
}
private bool ValidIdentifierChar(char value)
{
return (char.IsLetterOrDigit(_currentChar) || _currentChar == '_' || _currentChar == '$');
return (char.IsLetterOrDigit(value) || value == '_' || value == '$');
}
private void ParseUnquotedProperty()
private char ParseUnquotedProperty(char firstChar)
{
// parse unquoted property name until whitespace or colon
_buffer.Append(_currentChar);
_buffer.Append(firstChar);
while (MoveNext())
char currentChar;
while ((currentChar = MoveNext()) != '\0' || !_end)
{
if (char.IsWhiteSpace(_currentChar) || _currentChar == ':')
if (char.IsWhiteSpace(currentChar) || currentChar == ':')
{
break;
return currentChar;
}
else if (ValidIdentifierChar(_currentChar))
else if (ValidIdentifierChar(currentChar))
{
_buffer.Append(_currentChar);
_buffer.Append(currentChar);
}
else
{
throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
}
throw CreateJsonReaderException("Unexpected end when parsing unquoted property name. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition);
}
private bool ParseValue()
private bool ParseValue(char currentChar)
{
do
{
switch (_currentChar)
switch (currentChar)
{
case '"':
case '\'':
ParseString(_currentChar);
ParseString(currentChar);
return true;
case 't':
ParseTrue();
@@ -570,14 +541,14 @@ namespace Newtonsoft.Json
case 'n':
if (HasNext())
{
char next = PeekNext();
char next = (char)PeekNext();
if (next == 'u')
ParseNull();
else if (next == 'e')
ParseConstructor();
else
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
else
{
@@ -594,7 +565,7 @@ namespace Newtonsoft.Json
if (PeekNext() == 'I')
ParseNumberNegativeInfinity();
else
ParseNumber();
ParseNumber(currentChar);
return true;
case '/':
ParseComment();
@@ -616,63 +587,70 @@ namespace Newtonsoft.Json
return true;
case ',':
SetToken(JsonToken.Undefined);
//ClearCurrentChar();
return true;
case ')':
SetToken(JsonToken.EndConstructor);
return true;
case ' ':
case StringUtils.Tab:
case StringUtils.LineFeed:
case StringUtils.CarriageReturn:
// eat
break;
default:
if (char.IsWhiteSpace(_currentChar))
if (char.IsWhiteSpace(currentChar))
{
// eat
}
else if (char.IsNumber(_currentChar) || _currentChar == '-' || _currentChar == '.')
else if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.')
{
ParseNumber();
ParseNumber(currentChar);
return true;
}
else
{
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
}
break;
}
} while (MoveNext());
} while ((currentChar = MoveNext()) != '\0' || !_end);
return false;
}
private bool EatWhitespace(bool oneOrMore)
private bool EatWhitespace(char initialChar, bool oneOrMore, out char finalChar)
{
bool whitespace = false;
while (_currentChar == ' ' || char.IsWhiteSpace(_currentChar))
char currentChar = initialChar;
while (currentChar == ' ' || char.IsWhiteSpace(currentChar))
{
whitespace = true;
MoveNext();
currentChar = MoveNext();
}
finalChar = currentChar;
return (!oneOrMore || whitespace);
}
private void ParseConstructor()
{
if (MatchValue("new", true))
if (MatchValue('n', "new", true))
{
if (EatWhitespace(true))
char currentChar = MoveNext();
if (EatWhitespace(currentChar, true, out currentChar))
{
while (char.IsLetter(_currentChar))
while (char.IsLetter(currentChar))
{
_buffer.Append(_currentChar);
MoveNext();
_buffer.Append(currentChar);
currentChar = MoveNext();
}
EatWhitespace(false);
EatWhitespace(currentChar, false, out currentChar);
if (_currentChar != '(')
throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition);
if (currentChar != '(')
throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition);
string constructorName = _buffer.ToString();
_buffer.Position = 0;
@@ -682,22 +660,25 @@ namespace Newtonsoft.Json
}
}
private void ParseNumber()
private void ParseNumber(char firstChar)
{
char currentChar = firstChar;
// parse until seperator character or end
bool end = false;
do
{
if (CurrentIsSeperator())
if (IsSeperator(currentChar))
{
end = true;
_lastChar = currentChar;
}
else
_buffer.Append(_currentChar);
{
_buffer.Append(currentChar);
}
} while (!end && MoveNext());
// hit the end of the reader before the number ended. clear the last number value
if (!end)
ClearCurrentChar();
} while (!end && ((currentChar = MoveNext()) != '\0' || !_end));
string number = _buffer.ToString();
object numberValue;
@@ -706,12 +687,12 @@ namespace Newtonsoft.Json
if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) == -1
&& number.IndexOf("e", StringComparison.OrdinalIgnoreCase) == -1)
{
numberValue = Convert.ToInt64(_buffer.ToString(), CultureInfo.InvariantCulture);
numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Integer;
}
else
{
numberValue = Convert.ToDouble(_buffer.ToString(), CultureInfo.InvariantCulture);
numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture);
numberType = JsonToken.Float;
}
@@ -724,30 +705,30 @@ namespace Newtonsoft.Json
{
// should have already parsed / character before reaching this method
MoveNext();
char currentChar = MoveNext();
if (_currentChar == '*')
if (currentChar == '*')
{
while (MoveNext())
while ((currentChar = MoveNext()) != '\0' || !_end)
{
if (_currentChar == '*')
if (currentChar == '*')
{
if (MoveNext())
if ((currentChar = MoveNext()) != '\0' || !_end)
{
if (_currentChar == '/')
if (currentChar == '/')
{
break;
}
else
{
_buffer.Append('*');
_buffer.Append(_currentChar);
_buffer.Append(currentChar);
}
}
}
else
{
_buffer.Append(_currentChar);
_buffer.Append(currentChar);
}
}
}
@@ -759,30 +740,30 @@ namespace Newtonsoft.Json
SetToken(JsonToken.Comment, _buffer.ToString());
_buffer.Position = 0;
ClearCurrentChar();
}
private bool MatchValue(string value)
private bool MatchValue(char firstChar, string value)
{
char currentChar = firstChar;
int i = 0;
do
{
if (_currentChar != value[i])
if (currentChar != value[i])
{
break;
}
i++;
}
while (i < value.Length && MoveNext());
while (i < value.Length && ((currentChar = MoveNext()) != '\0' || !_end));
return (i == value.Length);
}
private bool MatchValue(string value, bool noTrailingNonSeperatorCharacters)
private bool MatchValue(char firstChar, 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);
bool match = MatchValue(firstChar, value);
if (!noTrailingNonSeperatorCharacters)
{
@@ -790,17 +771,17 @@ namespace Newtonsoft.Json
}
else
{
bool matchAndNoTrainingNonSeperatorCharacters = (match && (!MoveNext() || CurrentIsSeperator()));
if (!CurrentIsSeperator())
ClearCurrentChar();
int c = PeekNext();
char next = (c != -1) ? (char) c : '\0';
bool matchAndNoTrainingNonSeperatorCharacters = (match && (next == '\0' || IsSeperator(next)));
return matchAndNoTrainingNonSeperatorCharacters;
}
}
private bool CurrentIsSeperator()
private bool IsSeperator(char c)
{
switch (_currentChar)
switch (c)
{
case '}':
case ']':
@@ -814,9 +795,12 @@ namespace Newtonsoft.Json
return true;
break;
case ' ':
case StringUtils.Tab:
case StringUtils.LineFeed:
case StringUtils.CarriageReturn:
return true;
default:
if (char.IsWhiteSpace(_currentChar))
if (char.IsWhiteSpace(c))
return true;
break;
}
@@ -829,7 +813,7 @@ namespace Newtonsoft.Json
// check characters equal 'true'
// and that it is followed by either a seperator character
// or the text ends
if (MatchValue(JsonConvert.True, true))
if (MatchValue('t', JsonConvert.True, true))
{
SetToken(JsonToken.Boolean, true);
}
@@ -841,7 +825,7 @@ namespace Newtonsoft.Json
private void ParseNull()
{
if (MatchValue(JsonConvert.Null, true))
if (MatchValue('n', JsonConvert.Null, true))
{
SetToken(JsonToken.Null);
}
@@ -853,7 +837,7 @@ namespace Newtonsoft.Json
private void ParseUndefined()
{
if (MatchValue(JsonConvert.Undefined, true))
if (MatchValue('u', JsonConvert.Undefined, true))
{
SetToken(JsonToken.Undefined);
}
@@ -865,7 +849,7 @@ namespace Newtonsoft.Json
private void ParseFalse()
{
if (MatchValue(JsonConvert.False, true))
if (MatchValue('f', JsonConvert.False, true))
{
SetToken(JsonToken.Boolean, false);
}
@@ -877,7 +861,7 @@ namespace Newtonsoft.Json
private void ParseNumberNegativeInfinity()
{
if (MatchValue(JsonConvert.NegativeInfinity, true))
if (MatchValue('-', JsonConvert.NegativeInfinity, true))
{
SetToken(JsonToken.Float, double.NegativeInfinity);
}
@@ -889,7 +873,7 @@ namespace Newtonsoft.Json
private void ParseNumberPositiveInfinity()
{
if (MatchValue(JsonConvert.PositiveInfinity, true))
if (MatchValue('I', JsonConvert.PositiveInfinity, true))
{
SetToken(JsonToken.Float, double.PositiveInfinity);
}
@@ -901,7 +885,7 @@ namespace Newtonsoft.Json
private void ParseNumberNaN()
{
if (MatchValue(JsonConvert.NaN, true))
if (MatchValue('N', JsonConvert.NaN, true))
{
SetToken(JsonToken.Float, double.NaN);
}
@@ -964,4 +948,4 @@ namespace Newtonsoft.Json
get { return _currentLinePosition; }
}
}
}
}
+10 -2
View File
@@ -288,7 +288,15 @@ namespace Newtonsoft.Json
public override void WriteValue(string value)
{
base.WriteValue(value);
WriteValueInternal((value != null) ? JsonConvert.ToString(value, _quoteChar) : JsonConvert.Null, JsonToken.String);
if (value == null)
WriteValueInternal(JsonConvert.Null, JsonToken.Null);
else
{
JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true);
//_writer.Write(JavaScriptUtils.EscapeJavaScriptString(value, _quoteChar, false));
//WriteValueInternal(, JsonToken.String);
//JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true);
}
}
/// <summary>
@@ -428,7 +436,7 @@ namespace Newtonsoft.Json
public override void WriteValue(DateTime value)
{
base.WriteValue(value);
WriteValueInternal(JsonConvert.ToString(value), JsonToken.Date);
JsonConvert.WriteDateTimeString(_writer, value);
}
/// <summary>
+10 -10
View File
@@ -107,17 +107,17 @@ namespace Newtonsoft.Json
}
// array that gives a new state based on the current state an the token being written
private static readonly State[,] stateArray = {
private static readonly State[][] stateArray = new[] {
// Start PropertyName ObjectStart Object ArrayStart Array ConstructorStart Constructor Closed Error
//
/* None */{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error },
/* StartObject */{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.Error, State.Error },
/* StartArray */{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.Error, State.Error },
/* StartConstructor */{ State.ConstructorStart, State.ConstructorStart, State.Error, State.Error, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error, State.Error },
/* StartProperty */{ State.Property, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error },
/* Comment */{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
/* Raw */{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
/* Value */{ State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
/* None */new[]{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error },
/* StartObject */new[]{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.Error, State.Error },
/* StartArray */new[]{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.Error, State.Error },
/* StartConstructor */new[]{ State.ConstructorStart, State.ConstructorStart, State.Error, State.Error, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error, State.Error },
/* StartProperty */new[]{ State.Property, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error },
/* Comment */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
/* Raw */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
/* Value */new[]{ State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error },
};
private int _top;
@@ -586,7 +586,7 @@ namespace Newtonsoft.Json
}
// gets new state based on the current state and what is being written
State newState = stateArray[token, (int)_currentState];
State newState = stateArray[token][(int)_currentState];
if (newState == State.Error)
throw new JsonWriterException("Token {0} in state {1} would result in an invalid JavaScript object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString()));
@@ -209,7 +209,7 @@ namespace Newtonsoft.Json.Serialization
if (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true)
|| contract.CreatedType.IsValueType)
{
#if !PocketPC
#if !PocketPC && !SILVERLIGHT
contract.DefaultCreator = LateBoundDelegateFactory.CreateDefaultConstructor(contract.CreatedType);
#else
ConstructorInfo constructorInfo = ReflectionUtils.GetDefaultConstructor(contract.CreatedType, true);
@@ -414,7 +414,7 @@ namespace Newtonsoft.Json.Serialization
{
JsonProperty property = new JsonProperty();
property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member);
#if !PocketPC
#if !PocketPC && !SILVERLIGHT
property.ValueProvider = new DynamicValueProvider(member);
#else
property.ValueProvider = new ReflectionValueProvider(member);
@@ -23,7 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !PocketPC
#if !PocketPC && !SILVERLIGHT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -65,6 +65,20 @@ namespace Newtonsoft.Json.Serialization
if (_setter == null)
_setter = LateBoundDelegateFactory.CreateSet<object>(_memberInfo);
#if DEBUG
// dynamic method doesn't check whether the type is 'legal' to set
// add this check for unit tests
if (value == null)
{
if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo)))
throw new Exception("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo));
}
else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType()))
{
throw new Exception("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType()));
}
#endif
_setter(target, value);
}
catch (Exception ex)
@@ -82,7 +82,7 @@ namespace Newtonsoft.Json.Serialization
_genericWrapperType = ReflectionUtils.MakeGenericType(typeof(CollectionWrapper<>), CollectionItemType);
ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType });
#if !PocketPC
#if !PocketPC && !SILVERLIGHT
_genericWrapperCreator = LateBoundDelegateFactory.CreateMethodHandler(genericWrapperConstructor);
#else
_genericWrapperCreator = (target, args) => genericWrapperConstructor.Invoke(new[] { args[0] });
@@ -85,7 +85,7 @@ namespace Newtonsoft.Json.Serialization
_genericWrapperType = ReflectionUtils.MakeGenericType(typeof(DictionaryWrapper<,>), DictionaryKeyType, DictionaryValueType);
ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType });
#if !PocketPC
#if !PocketPC && !SILVERLIGHT
_genericWrapperCreator = LateBoundDelegateFactory.CreateMethodHandler(genericWrapperConstructor);
#else
_genericWrapperCreator = (target, args) => genericWrapperConstructor.Invoke(new[] { args[0] });
@@ -85,43 +85,38 @@ namespace Newtonsoft.Json.Serialization
}
/// <summary>
/// Tries to get the closest matching <see cref="JsonProperty"/> object.
/// Gets the closest matching <see cref="JsonProperty"/> object.
/// First attempts to get an exact case match of propertyName and then
/// a case insensitive match.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="property">A matching property if found.</param>
/// <returns>A flag indicating whether a match was found.</returns>
public bool TryGetClosestMatchProperty(string propertyName, out JsonProperty property)
/// <returns>A matching property if found.</returns>
public JsonProperty GetClosestMatchProperty(string propertyName)
{
if (TryGetProperty(propertyName, StringComparison.Ordinal, out property))
return true;
if (TryGetProperty(propertyName, StringComparison.OrdinalIgnoreCase, out property))
return true;
JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal);
if (property == null)
property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase);
return false;
return property;
}
/// <summary>
/// Tries to get a property by property name.
/// Gets a property by property name.
/// </summary>
/// <param name="propertyName">The name of the property to get.</param>
/// <param name="comparisonType">Type property name string comparison.</param>
/// <param name="matchingProperty">A matching property if found.</param>
/// <returns>A flag indicating whether a match was found.</returns>
public bool TryGetProperty(string propertyName, StringComparison comparisonType, out JsonProperty matchingProperty)
/// <returns>A matching property if found.</returns>
public JsonProperty GetProperty(string propertyName, StringComparison comparisonType)
{
foreach (JsonProperty property in this)
{
if (string.Compare(propertyName, property.PropertyName, comparisonType) == 0)
if (string.Equals(propertyName, property.PropertyName, comparisonType))
{
matchingProperty = property;
return true;
return property;
}
}
matchingProperty = default(JsonProperty);
return false;
return null;
}
}
}
@@ -171,8 +171,8 @@ namespace Newtonsoft.Json.Serialization
if (memberConverter != null)
return memberConverter.ReadJson(reader, objectType, GetInternalSerializer());
if (objectType != null && Serializer.HasClassConverter(contract, out converter))
return converter.ReadJson(reader, objectType, GetInternalSerializer());
if (contract != null && contract.Converter != null)
return contract.Converter.ReadJson(reader, objectType, GetInternalSerializer());
if (objectType != null && Serializer.HasMatchingConverter(objectType, out converter))
return converter.ReadJson(reader, objectType, GetInternalSerializer());
@@ -472,7 +472,7 @@ namespace Newtonsoft.Json.Serialization
return dictionary.UnderlyingDictionary;
}
private IDictionary PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id)
private object PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id)
{
if (id != null)
Serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary);
@@ -491,7 +491,7 @@ namespace Newtonsoft.Json.Serialization
try
{
dictionary.Add(keyValue, CreateValue(reader, contract.DictionaryValueType, GetContractSafe(contract.DictionaryValueType), null, null));
dictionary[keyValue] = CreateValue(reader, contract.DictionaryValueType, GetContractSafe(contract.DictionaryValueType), null, null);
}
catch (Exception ex)
{
@@ -504,7 +504,7 @@ namespace Newtonsoft.Json.Serialization
case JsonToken.EndObject:
contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary);
return dictionary;
return dictionary.UnderlyingDictionary;
default:
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
}
@@ -615,10 +615,11 @@ namespace Newtonsoft.Json.Serialization
if (!reader.Read())
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
JsonProperty property;
// attempt exact case match first
// then try match ignoring case
if (contract.Properties.TryGetClosestMatchProperty(memberName, out property))
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
if (property != null)
{
if (!property.Ignored)
{
@@ -693,10 +694,11 @@ namespace Newtonsoft.Json.Serialization
case JsonToken.PropertyName:
string memberName = reader.Value.ToString();
JsonProperty property;
// attempt exact case match first
// then try match ignoring case
if (!contract.Properties.TryGetClosestMatchProperty(memberName, out property))
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
if (property == null)
{
if (Serializer.MissingMemberHandling == MissingMemberHandling.Error)
throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name));
@@ -91,7 +91,7 @@ namespace Newtonsoft.Json.Serialization
}
if (converter != null
|| Serializer.HasClassConverter(contract, out converter)
|| ((converter = contract.Converter) != null)
|| Serializer.HasMatchingConverter(contract.UnderlyingType, out converter))
{
SerializeConvertable(writer, converter, value, contract);
@@ -165,7 +165,7 @@ namespace Newtonsoft.Json.Serialization
return Serializer.ReferenceResolver.IsReferenced(value);
}
private void WriteMemberInfoProperty(JsonWriter writer, object value, object memberValue, JsonProperty property, JsonContract contract)
private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract)
{
string propertyName = property.PropertyName;
JsonConverter memberConverter = property.MemberConverter;
@@ -295,7 +295,7 @@ namespace Newtonsoft.Json.Serialization
object memberValue = property.ValueProvider.GetValue(value);
JsonContract memberContract = GetContractSafe(memberValue);
WriteMemberInfoProperty(writer, value, memberValue, property, memberContract);
WriteMemberInfoProperty(writer, memberValue, property, memberContract);
}
}
catch (Exception ex)
@@ -314,6 +314,10 @@ namespace Newtonsoft.Json.Utilities
public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType)
{
object convertedValue;
if (initialValue == null && ReflectionUtils.IsNullable(targetType))
return null;
if (TryConvert(initialValue, culture, targetType, out convertedValue))
return convertedValue;
@@ -35,68 +35,100 @@ namespace Newtonsoft.Json.Utilities
{
internal static class JavaScriptUtils
{
public static void WriteEscapedJavaScriptString(StringBuilder writer, string value, char delimiter, bool appendDelimiters)
public static void WriteEscapedJavaScriptString(TextWriter writer, string value, char delimiter, bool appendDelimiters)
{
// leading delimiter
if (appendDelimiters)
writer.Append(delimiter);
writer.Write(delimiter);
if (value != null)
{
int lastWritePosition = 0;
int skipped = 0;
char[] chars = null;
for (int i = 0; i < value.Length; i++)
{
char c = value[i];
string escapedValue;
switch (c)
{
case '\t':
writer.Append(@"\t");
escapedValue = @"\t";
break;
case '\n':
writer.Append(@"\n");
escapedValue = @"\n";
break;
case '\r':
writer.Append(@"\r");
escapedValue = @"\r";
break;
case '\f':
writer.Append(@"\f");
escapedValue = @"\f";
break;
case '\b':
writer.Append(@"\b");
escapedValue = @"\b";
break;
case '\\':
writer.Append(@"\\");
escapedValue = @"\\";
break;
case '\u0085': // Next Line
writer.Append(@"\u0085");
escapedValue = @"\u0085";
break;
case '\u2028': // Line Separator
writer.Append(@"\u2028");
escapedValue = @"\u2028";
break;
case '\u2029': // Paragraph Separator
writer.Append(@"\u2029");
escapedValue = @"\u2029";
break;
case '\'':
// only escape if this charater is being used as the delimiter
writer.Append((delimiter == '\'') ? @"\'" : @"'");
escapedValue = (delimiter == '\'') ? @"\'" : null;
break;
case '"':
// only escape if this charater is being used as the delimiter
writer.Append((delimiter == '"') ? "\\\"" : @"""");
escapedValue = (delimiter == '"') ? "\\\"" : null;
break;
default:
if (c > '\u001f')
writer.Append(c);
else
StringUtils.WriteCharAsUnicode(writer, c);
escapedValue = (c <= '\u001f') ? StringUtils.ToCharAsUnicode(c) : null;
break;
}
if (escapedValue != null)
{
if (chars == null)
chars = value.ToCharArray();
// write skipped text
if (skipped > 0)
{
writer.Write(chars, lastWritePosition, skipped);
skipped = 0;
}
// write escaped value and note position
writer.Write(escapedValue);
lastWritePosition = i + 1;
}
else
{
skipped++;
}
}
// write any remaining skipped text
if (skipped > 0)
{
if (lastWritePosition == 0)
writer.Write(value);
else
writer.Write(chars, lastWritePosition, skipped);
}
}
// trailing delimiter
if (appendDelimiters)
writer.Append(delimiter);
writer.Write(delimiter);
}
public static string ToEscapedJavaScriptString(string value)
@@ -106,14 +138,11 @@ namespace Newtonsoft.Json.Utilities
public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters)
{
int length = value.Length;
if (appendDelimiters)
length += 2;
StringBuilder builder = new StringBuilder(length);
WriteEscapedJavaScriptString(builder, value, delimiter, appendDelimiters);
return builder.ToString();
using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16))
{
WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters);
return w.ToString();
}
}
}
}
@@ -1,4 +1,29 @@
#if !PocketPC
#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
#if !PocketPC && !SILVERLIGHT
using System;
using System.Collections.Generic;
using System.Linq;
@@ -56,7 +56,7 @@ namespace Newtonsoft.Json.Utilities
public void Append(char value)
{
// test if the buffer array is large enough to take the value
if (_position + 1 > _buffer.Length)
if (_position == _buffer.Length)
{
EnsureSize(1);
}
+12 -10
View File
@@ -325,13 +325,15 @@ namespace Newtonsoft.Json.Utilities
public static string ToCharAsUnicode(char c)
{
StringBuilder builder = new StringBuilder();
WriteCharAsUnicode(builder, c);
char h1 = MathUtils.IntToHex((c >> 12) & '\x000f');
char h2 = MathUtils.IntToHex((c >> 8) & '\x000f');
char h3 = MathUtils.IntToHex((c >> 4) & '\x000f');
char h4 = MathUtils.IntToHex(c & '\x000f');
return builder.ToString();
return new string(new[] { '\\', 'u', h1, h2, h3, h4 });
}
public static void WriteCharAsUnicode(StringBuilder writer, char c)
public static void WriteCharAsUnicode(TextWriter writer, char c)
{
ValidationUtils.ArgumentNotNull(writer, "writer");
@@ -340,12 +342,12 @@ namespace Newtonsoft.Json.Utilities
char h3 = MathUtils.IntToHex((c >> 4) & '\x000f');
char h4 = MathUtils.IntToHex(c & '\x000f');
writer.Append('\\');
writer.Append('u');
writer.Append(h1);
writer.Append(h2);
writer.Append(h3);
writer.Append(h4);
writer.Write('\\');
writer.Write('u');
writer.Write(h1);
writer.Write(h2);
writer.Write(h3);
writer.Write(h4);
}
public static TSource ForgivingCaseSensitiveFind<TSource>(this IEnumerable<TSource> source, Func<TSource, string> valueSelector, string testValue)