diff --git a/Libs/EpicMorg.Net.AWC.dll b/Libs/EpicMorg.Net.AWC.dll deleted file mode 100644 index 72a4bd4..0000000 Binary files a/Libs/EpicMorg.Net.AWC.dll and /dev/null differ diff --git a/Libs/JsonNet/Bson/BsonBinaryType.cs b/Libs/JsonNet/Bson/BsonBinaryType.cs new file mode 100644 index 0000000..638039f --- /dev/null +++ b/Libs/JsonNet/Bson/BsonBinaryType.cs @@ -0,0 +1,44 @@ +#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; + +namespace Newtonsoft.Json.Bson +{ + internal enum BsonBinaryType : byte + { + Binary = 0x00, + Function = 0x01, + + [Obsolete("This type has been deprecated in the BSON specification. Use Binary instead.")] + BinaryOld = 0x02, + + [Obsolete("This type has been deprecated in the BSON specification. Use Uuid instead.")] + UuidOld = 0x03, + Uuid = 0x04, + Md5 = 0x05, + UserDefined = 0x80 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonBinaryWriter.cs b/Libs/JsonNet/Bson/BsonBinaryWriter.cs new file mode 100644 index 0000000..b0f4026 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonBinaryWriter.cs @@ -0,0 +1,325 @@ +#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.Globalization; +using System.IO; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Bson +{ + internal class BsonBinaryWriter + { + private static readonly Encoding Encoding = new UTF8Encoding(false); + + private readonly BinaryWriter _writer; + + private byte[] _largeByteBuffer; + + public DateTimeKind DateTimeKindHandling { get; set; } + + public BsonBinaryWriter(BinaryWriter writer) + { + DateTimeKindHandling = DateTimeKind.Utc; + _writer = writer; + } + + public void Flush() + { + _writer.Flush(); + } + + public void Close() + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + _writer.Close(); +#else + _writer.Dispose(); +#endif + } + + public void WriteToken(BsonToken t) + { + CalculateSize(t); + WriteTokenInternal(t); + } + + private void WriteTokenInternal(BsonToken t) + { + switch (t.Type) + { + case BsonType.Object: + { + BsonObject value = (BsonObject)t; + _writer.Write(value.CalculatedSize); + foreach (BsonProperty property in value) + { + _writer.Write((sbyte)property.Value.Type); + WriteString((string)property.Name.Value, property.Name.ByteCount, null); + WriteTokenInternal(property.Value); + } + _writer.Write((byte)0); + } + break; + case BsonType.Array: + { + BsonArray value = (BsonArray)t; + _writer.Write(value.CalculatedSize); + ulong index = 0; + foreach (BsonToken c in value) + { + _writer.Write((sbyte)c.Type); + WriteString(index.ToString(CultureInfo.InvariantCulture), MathUtils.IntLength(index), null); + WriteTokenInternal(c); + index++; + } + _writer.Write((byte)0); + } + break; + case BsonType.Integer: + { + BsonValue value = (BsonValue)t; + _writer.Write(Convert.ToInt32(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.Long: + { + BsonValue value = (BsonValue)t; + _writer.Write(Convert.ToInt64(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.Number: + { + BsonValue value = (BsonValue)t; + _writer.Write(Convert.ToDouble(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.String: + { + BsonString value = (BsonString)t; + WriteString((string)value.Value, value.ByteCount, value.CalculatedSize - 4); + } + break; + case BsonType.Boolean: + { + BsonValue value = (BsonValue)t; + _writer.Write((bool)value.Value); + } + break; + case BsonType.Null: + case BsonType.Undefined: + break; + case BsonType.Date: + { + BsonValue value = (BsonValue)t; + + long ticks = 0; + + if (value.Value is DateTime) + { + DateTime dateTime = (DateTime)value.Value; + if (DateTimeKindHandling == DateTimeKind.Utc) + dateTime = dateTime.ToUniversalTime(); + else if (DateTimeKindHandling == DateTimeKind.Local) + dateTime = dateTime.ToLocalTime(); + + ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTime, false); + } +#if !NET20 + else + { + DateTimeOffset dateTimeOffset = (DateTimeOffset)value.Value; + ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset); + } +#endif + + _writer.Write(ticks); + } + break; + case BsonType.Binary: + { + BsonBinary value = (BsonBinary)t; + + byte[] data = (byte[])value.Value; + _writer.Write(data.Length); + _writer.Write((byte)value.BinaryType); + _writer.Write(data); + } + break; + case BsonType.Oid: + { + BsonValue value = (BsonValue)t; + + byte[] data = (byte[])value.Value; + _writer.Write(data); + } + break; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex)t; + + WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null); + WriteString((string)value.Options.Value, value.Options.ByteCount, null); + } + break; + default: + throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); + } + } + + private void WriteString(string s, int byteCount, int? calculatedlengthPrefix) + { + if (calculatedlengthPrefix != null) + _writer.Write(calculatedlengthPrefix.Value); + + WriteUtf8Bytes(s, byteCount); + + _writer.Write((byte)0); + } + + public void WriteUtf8Bytes(string s, int byteCount) + { + if (s != null) + { + if (_largeByteBuffer == null) + { + _largeByteBuffer = new byte[256]; + } + if (byteCount <= 256) + { + Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0); + _writer.Write(_largeByteBuffer, 0, byteCount); + } + else + { + byte[] bytes = Encoding.GetBytes(s); + _writer.Write(bytes); + } + } + } + + private int CalculateSize(int stringByteCount) + { + return stringByteCount + 1; + } + + private int CalculateSizeWithLength(int stringByteCount, bool includeSize) + { + int baseSize = (includeSize) + ? 5 // size bytes + terminator + : 1; // terminator + + return baseSize + stringByteCount; + } + + private int CalculateSize(BsonToken t) + { + switch (t.Type) + { + case BsonType.Object: + { + BsonObject value = (BsonObject)t; + + int bases = 4; + foreach (BsonProperty p in value) + { + int size = 1; + size += CalculateSize(p.Name); + size += CalculateSize(p.Value); + + bases += size; + } + bases += 1; + value.CalculatedSize = bases; + return bases; + } + case BsonType.Array: + { + BsonArray value = (BsonArray)t; + + int size = 4; + ulong index = 0; + foreach (BsonToken c in value) + { + size += 1; + size += CalculateSize(MathUtils.IntLength(index)); + size += CalculateSize(c); + index++; + } + size += 1; + value.CalculatedSize = size; + + return value.CalculatedSize; + } + case BsonType.Integer: + return 4; + case BsonType.Long: + return 8; + case BsonType.Number: + return 8; + case BsonType.String: + { + BsonString value = (BsonString)t; + string s = (string)value.Value; + value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0; + value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength); + + return value.CalculatedSize; + } + case BsonType.Boolean: + return 1; + case BsonType.Null: + case BsonType.Undefined: + return 0; + case BsonType.Date: + return 8; + case BsonType.Binary: + { + BsonBinary value = (BsonBinary)t; + + byte[] data = (byte[])value.Value; + value.CalculatedSize = 4 + 1 + data.Length; + + return value.CalculatedSize; + } + case BsonType.Oid: + return 12; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex)t; + int size = 0; + size += CalculateSize(value.Pattern); + size += CalculateSize(value.Options); + value.CalculatedSize = size; + + return value.CalculatedSize; + } + default: + throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonObjectId.cs b/Libs/JsonNet/Bson/BsonObjectId.cs new file mode 100644 index 0000000..399e5f4 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonObjectId.cs @@ -0,0 +1,55 @@ +#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 Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a BSON Oid (object id). + /// + public class BsonObjectId + { + /// + /// Gets or sets the value of the Oid. + /// + /// The value of the Oid. + public byte[] Value { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The Oid value. + public BsonObjectId(byte[] value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + if (value.Length != 12) + throw new ArgumentException("An ObjectId must be 12 bytes", "value"); + + Value = value; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonReader.cs b/Libs/JsonNet/Bson/BsonReader.cs new file mode 100644 index 0000000..ccd5662 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonReader.cs @@ -0,0 +1,856 @@ +#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.Globalization; +using System.Text; +using System.IO; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class BsonReader : JsonReader + { + private const int MaxCharBytesSize = 128; + private static readonly byte[] SeqRange1 = new byte[] { 0, 127 }; // range of 1-byte sequence + private static readonly byte[] SeqRange2 = new byte[] { 194, 223 }; // range of 2-byte sequence + private static readonly byte[] SeqRange3 = new byte[] { 224, 239 }; // range of 3-byte sequence + private static readonly byte[] SeqRange4 = new byte[] { 240, 244 }; // range of 4-byte sequence + + private readonly BinaryReader _reader; + private readonly List _stack; + + private byte[] _byteBuffer; + private char[] _charBuffer; + + private BsonType _currentElementType; + private BsonReaderState _bsonReaderState; + private ContainerContext _currentContext; + + private bool _readRootValueAsArray; + private bool _jsonNet35BinaryCompatibility; + private DateTimeKind _dateTimeKindHandling; + + private enum BsonReaderState + { + Normal, + ReferenceStart, + ReferenceRef, + ReferenceId, + CodeWScopeStart, + CodeWScopeCode, + CodeWScopeScope, + CodeWScopeScopeObject, + CodeWScopeScopeEnd + } + + private class ContainerContext + { + public readonly BsonType Type; + public int Length; + public int Position; + + public ContainerContext(BsonType type) + { + Type = type; + } + } + + /// + /// Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary. + /// + /// + /// true if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, false. + /// + [Obsolete("JsonNet35BinaryCompatibility will be removed in a future version of Json.NET.")] + public bool JsonNet35BinaryCompatibility + { + get { return _jsonNet35BinaryCompatibility; } + set { _jsonNet35BinaryCompatibility = value; } + } + + /// + /// Gets or sets a value indicating whether the root object will be read as a JSON array. + /// + /// + /// true if the root object will be read as a JSON array; otherwise, false. + /// + public bool ReadRootValueAsArray + { + get { return _readRootValueAsArray; } + set { _readRootValueAsArray = value; } + } + + /// + /// Gets or sets the used when reading values from BSON. + /// + /// The used when reading values from BSON. + public DateTimeKind DateTimeKindHandling + { + get { return _dateTimeKindHandling; } + set { _dateTimeKindHandling = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public BsonReader(Stream stream) + : this(stream, false, DateTimeKind.Local) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The reader. + public BsonReader(BinaryReader reader) + : this(reader, false, DateTimeKind.Local) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// if set to true the root object will be read as a JSON array. + /// The used when reading values from BSON. + public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling) + { + ValidationUtils.ArgumentNotNull(stream, "stream"); + _reader = new BinaryReader(stream); + _stack = new List(); + _readRootValueAsArray = readRootValueAsArray; + _dateTimeKindHandling = dateTimeKindHandling; + } + + /// + /// Initializes a new instance of the class. + /// + /// The reader. + /// if set to true the root object will be read as a JSON array. + /// The used when reading values from BSON. + public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + _reader = reader; + _stack = new List(); + _readRootValueAsArray = readRootValueAsArray; + _dateTimeKindHandling = dateTimeKindHandling; + } + + private string ReadElement() + { + _currentElementType = ReadType(); + string elementName = ReadString(); + return elementName; + } + + /// + /// Reads the next JSON token from the stream as a []. + /// + /// + /// A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. + /// + public override byte[] ReadAsBytes() + { + return ReadAsBytesInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override decimal? ReadAsDecimal() + { + return ReadAsDecimalInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override int? ReadAsInt32() + { + return ReadAsInt32Internal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override string ReadAsString() + { + return ReadAsStringInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTime? ReadAsDateTime() + { + return ReadAsDateTimeInternal(); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A . This method will return null at the end of an array. + /// + public override DateTimeOffset? ReadAsDateTimeOffset() + { + return ReadAsDateTimeOffsetInternal(); + } +#endif + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + _readType = Json.ReadType.Read; + + return ReadInternal(); + } + + internal override bool ReadInternal() + { + try + { + bool success; + + switch (_bsonReaderState) + { + case BsonReaderState.Normal: + success = ReadNormal(); + break; + case BsonReaderState.ReferenceStart: + case BsonReaderState.ReferenceRef: + case BsonReaderState.ReferenceId: + success = ReadReference(); + break; + case BsonReaderState.CodeWScopeStart: + case BsonReaderState.CodeWScopeCode: + case BsonReaderState.CodeWScopeScope: + case BsonReaderState.CodeWScopeScopeObject: + case BsonReaderState.CodeWScopeScopeEnd: + success = ReadCodeWScope(); + break; + default: + throw JsonReaderException.Create(this, "Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState)); + } + + if (!success) + { + SetToken(JsonToken.None); + return false; + } + + return true; + } + catch (EndOfStreamException) + { + SetToken(JsonToken.None); + return false; + } + } + + /// + /// Changes the to Closed. + /// + public override void Close() + { + base.Close(); + + if (CloseInput && _reader != null) +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + _reader.Close(); +#else + _reader.Dispose(); +#endif + } + + private bool ReadCodeWScope() + { + switch (_bsonReaderState) + { + case BsonReaderState.CodeWScopeStart: + SetToken(JsonToken.PropertyName, "$code"); + _bsonReaderState = BsonReaderState.CodeWScopeCode; + return true; + case BsonReaderState.CodeWScopeCode: + // total CodeWScope size - not used + ReadInt32(); + + SetToken(JsonToken.String, ReadLengthString()); + _bsonReaderState = BsonReaderState.CodeWScopeScope; + return true; + case BsonReaderState.CodeWScopeScope: + if (CurrentState == State.PostValue) + { + SetToken(JsonToken.PropertyName, "$scope"); + return true; + } + else + { + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeScopeObject; + + ContainerContext newContext = new ContainerContext(BsonType.Object); + PushContext(newContext); + newContext.Length = ReadInt32(); + + return true; + } + case BsonReaderState.CodeWScopeScopeObject: + bool result = ReadNormal(); + if (result && TokenType == JsonToken.EndObject) + _bsonReaderState = BsonReaderState.CodeWScopeScopeEnd; + + return result; + case BsonReaderState.CodeWScopeScopeEnd: + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private bool ReadReference() + { + switch (CurrentState) + { + case State.ObjectStart: + { + SetToken(JsonToken.PropertyName, JsonTypeReflector.RefPropertyName); + _bsonReaderState = BsonReaderState.ReferenceRef; + return true; + } + case State.Property: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.String, ReadLengthString()); + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.Bytes, ReadBytes(12)); + return true; + } + else + { + throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + case State.PostValue: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.PropertyName, JsonTypeReflector.IdPropertyName); + _bsonReaderState = BsonReaderState.ReferenceId; + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + } + else + { + throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + default: + throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + CurrentState); + } + } + + private bool ReadNormal() + { + switch (CurrentState) + { + case State.Start: + { + JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray; + BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array; + + SetToken(token); + ContainerContext newContext = new ContainerContext(type); + PushContext(newContext); + newContext.Length = ReadInt32(); + return true; + } + case State.Complete: + case State.Closed: + return false; + case State.Property: + { + ReadType(_currentElementType); + return true; + } + case State.ObjectStart: + case State.ArrayStart: + case State.PostValue: + ContainerContext context = _currentContext; + if (context == null) + return false; + + int lengthMinusEnd = context.Length - 1; + + if (context.Position < lengthMinusEnd) + { + if (context.Type == BsonType.Array) + { + ReadElement(); + ReadType(_currentElementType); + return true; + } + else + { + SetToken(JsonToken.PropertyName, ReadElement()); + return true; + } + } + else if (context.Position == lengthMinusEnd) + { + if (ReadByte() != 0) + throw JsonReaderException.Create(this, "Unexpected end of object byte value."); + + PopContext(); + if (_currentContext != null) + MovePosition(context.Length); + + JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray; + SetToken(endToken); + return true; + } + else + { + throw JsonReaderException.Create(this, "Read past end of current container context."); + } + case State.ConstructorStart: + break; + case State.Constructor: + break; + case State.Error: + break; + case State.Finished: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + 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); + return _reader.ReadByte(); + } + + private void ReadType(BsonType type) + { + switch (type) + { + case BsonType.Number: + double d = ReadDouble(); + + if (_floatParseHandling == FloatParseHandling.Decimal) + SetToken(JsonToken.Float, Convert.ToDecimal(d, CultureInfo.InvariantCulture)); + else + SetToken(JsonToken.Float, d); + break; + case BsonType.String: + case BsonType.Symbol: + SetToken(JsonToken.String, ReadLengthString()); + break; + case BsonType.Object: + { + SetToken(JsonToken.StartObject); + + ContainerContext newContext = new ContainerContext(BsonType.Object); + PushContext(newContext); + newContext.Length = ReadInt32(); + break; + } + case BsonType.Array: + { + SetToken(JsonToken.StartArray); + + ContainerContext newContext = new ContainerContext(BsonType.Array); + PushContext(newContext); + newContext.Length = ReadInt32(); + break; + } + case BsonType.Binary: + BsonBinaryType binaryType; + byte[] data = ReadBinary(out binaryType); + + object value = (binaryType != BsonBinaryType.Uuid) + ? data + : (object)new Guid(data); + + SetToken(JsonToken.Bytes, value); + break; + case BsonType.Undefined: + SetToken(JsonToken.Undefined); + break; + case BsonType.Oid: + byte[] oid = ReadBytes(12); + SetToken(JsonToken.Bytes, oid); + break; + case BsonType.Boolean: + bool b = Convert.ToBoolean(ReadByte()); + SetToken(JsonToken.Boolean, b); + break; + case BsonType.Date: + long ticks = ReadInt64(); + DateTime utcDateTime = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ticks); + + DateTime dateTime; + switch (DateTimeKindHandling) + { + case DateTimeKind.Unspecified: + dateTime = DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified); + break; + case DateTimeKind.Local: + dateTime = utcDateTime.ToLocalTime(); + break; + default: + dateTime = utcDateTime; + break; + } + + SetToken(JsonToken.Date, dateTime); + break; + case BsonType.Null: + SetToken(JsonToken.Null); + break; + case BsonType.Regex: + string expression = ReadString(); + string modifiers = ReadString(); + + string regex = @"/" + expression + @"/" + modifiers; + SetToken(JsonToken.String, regex); + break; + case BsonType.Reference: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.ReferenceStart; + break; + case BsonType.Code: + SetToken(JsonToken.String, ReadLengthString()); + break; + case BsonType.CodeWScope: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeStart; + break; + case BsonType.Integer: + SetToken(JsonToken.Integer, (long)ReadInt32()); + break; + case BsonType.TimeStamp: + case BsonType.Long: + SetToken(JsonToken.Integer, ReadInt64()); + break; + default: + throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type); + } + } + + private byte[] ReadBinary(out BsonBinaryType binaryType) + { + int dataLength = ReadInt32(); + + binaryType = (BsonBinaryType)ReadByte(); + +#pragma warning disable 612,618 + // the old binary type has the data length repeated in the data for some reason + if (binaryType == BsonBinaryType.BinaryOld && !_jsonNet35BinaryCompatibility) + { + dataLength = ReadInt32(); + } +#pragma warning restore 612,618 + + return ReadBytes(dataLength); + } + + private string ReadString() + { + EnsureBuffers(); + + StringBuilder builder = null; + + int totalBytesRead = 0; + // used in case of left over multibyte characters in the buffer + int offset = 0; + do + { + int count = offset; + byte b; + while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0) + { + _byteBuffer[count++] = b; + } + int byteCount = count - offset; + totalBytesRead += byteCount; + + if (count < MaxCharBytesSize && builder == null) + { + // pref optimization to avoid reading into a string builder + // if string is smaller than the buffer then return it directly + int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0); + + MovePosition(totalBytesRead + 1); + return new string(_charBuffer, 0, length); + } + else + { + // calculate the index of the end of the last full character in the buffer + int lastFullCharStop = GetLastFullCharStop(count - 1); + + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); + + if (builder == null) + builder = new StringBuilder(MaxCharBytesSize * 2); + + builder.Append(_charBuffer, 0, charCount); + + if (lastFullCharStop < byteCount - 1) + { + offset = byteCount - lastFullCharStop - 1; + // copy left over multi byte characters to beginning of buffer for next iteration + Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset); + } + else + { + // reached end of string + if (count < MaxCharBytesSize) + { + MovePosition(totalBytesRead + 1); + return builder.ToString(); + } + + offset = 0; + } + } + } while (true); + } + + private string ReadLengthString() + { + int length = ReadInt32(); + + MovePosition(length); + + string s = GetString(length - 1); + _reader.ReadByte(); + + return s; + } + + private string GetString(int length) + { + if (length == 0) + return string.Empty; + + EnsureBuffers(); + + StringBuilder builder = null; + + int totalBytesRead = 0; + + // used in case of left over multibyte characters in the buffer + int offset = 0; + do + { + int count = ((length - totalBytesRead) > MaxCharBytesSize - offset) + ? MaxCharBytesSize - offset + : length - totalBytesRead; + + int byteCount = _reader.Read(_byteBuffer, offset, count); + + if (byteCount == 0) + throw new EndOfStreamException("Unable to read beyond the end of the stream."); + + totalBytesRead += byteCount; + + // Above, byteCount is how many bytes we read this time. + // Below, byteCount is how many bytes are in the _byteBuffer. + byteCount += offset; + + if (byteCount == length) + { + // pref optimization to avoid reading into a string builder + // first iteration and all bytes read then return string directly + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0); + return new string(_charBuffer, 0, charCount); + } + else + { + int lastFullCharStop = GetLastFullCharStop(byteCount - 1); + + if (builder == null) + builder = new StringBuilder(length); + + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); + builder.Append(_charBuffer, 0, charCount); + + if (lastFullCharStop < byteCount - 1) + { + offset = byteCount - lastFullCharStop - 1; + // copy left over multi byte characters to beginning of buffer for next iteration + Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset); + } + else + { + offset = 0; + } + } + } while (totalBytesRead < length); + + return builder.ToString(); + } + + private int GetLastFullCharStop(int start) + { + int lookbackPos = start; + int bis = 0; + while (lookbackPos >= 0) + { + bis = BytesInSequence(_byteBuffer[lookbackPos]); + if (bis == 0) + { + lookbackPos--; + continue; + } + else if (bis == 1) + { + break; + } + else + { + lookbackPos--; + break; + } + } + if (bis == start - lookbackPos) + { + //Full character. + return start; + } + else + { + return lookbackPos; + } + } + + private int BytesInSequence(byte b) + { + if (b <= SeqRange1[1]) return 1; + if (b >= SeqRange2[0] && b <= SeqRange2[1]) return 2; + if (b >= SeqRange3[0] && b <= SeqRange3[1]) return 3; + if (b >= SeqRange4[0] && b <= SeqRange4[1]) return 4; + return 0; + } + + private void EnsureBuffers() + { + if (_byteBuffer == null) + { + _byteBuffer = new byte[MaxCharBytesSize]; + } + if (_charBuffer == null) + { + int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize); + _charBuffer = new char[charBufferSize]; + } + } + + private double ReadDouble() + { + MovePosition(8); + return _reader.ReadDouble(); + } + + private int ReadInt32() + { + MovePosition(4); + return _reader.ReadInt32(); + } + + private long ReadInt64() + { + MovePosition(8); + return _reader.ReadInt64(); + } + + private BsonType ReadType() + { + MovePosition(1); + return (BsonType)_reader.ReadSByte(); + } + + private void MovePosition(int count) + { + _currentContext.Position += count; + } + + private byte[] ReadBytes(int count) + { + MovePosition(count); + return _reader.ReadBytes(count); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonToken.cs b/Libs/JsonNet/Bson/BsonToken.cs new file mode 100644 index 0000000..06927a4 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonToken.cs @@ -0,0 +1,157 @@ +#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.Collections; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Bson +{ + internal abstract class BsonToken + { + public abstract BsonType Type { get; } + public BsonToken Parent { get; set; } + public int CalculatedSize { get; set; } + } + + internal class BsonObject : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(string name, BsonToken token) + { + _children.Add(new BsonProperty { Name = new BsonString(name, false), Value = token }); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Object; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonArray : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(BsonToken token) + { + _children.Add(token); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Array; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonValue : BsonToken + { + private readonly object _value; + private readonly BsonType _type; + + public BsonValue(object value, BsonType type) + { + _value = value; + _type = type; + } + + public object Value + { + get { return _value; } + } + + public override BsonType Type + { + get { return _type; } + } + } + + internal class BsonString : BsonValue + { + public int ByteCount { get; set; } + public bool IncludeLength { get; set; } + + public BsonString(object value, bool includeLength) + : base(value, BsonType.String) + { + IncludeLength = includeLength; + } + } + + internal class BsonBinary : BsonValue + { + public BsonBinaryType BinaryType { get; set; } + + public BsonBinary(byte[] value, BsonBinaryType binaryType) + : base(value, BsonType.Binary) + { + BinaryType = binaryType; + } + } + + internal class BsonRegex : BsonToken + { + public BsonString Pattern { get; set; } + public BsonString Options { get; set; } + + public BsonRegex(string pattern, string options) + { + Pattern = new BsonString(pattern, false); + Options = new BsonString(options, false); + } + + public override BsonType Type + { + get { return BsonType.Regex; } + } + } + + internal class BsonProperty + { + public BsonString Name { get; set; } + public BsonToken Value { get; set; } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonType.cs b/Libs/JsonNet/Bson/BsonType.cs new file mode 100644 index 0000000..e119298 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonType.cs @@ -0,0 +1,51 @@ +#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 + +namespace Newtonsoft.Json.Bson +{ + internal enum BsonType : sbyte + { + Number = 1, + String = 2, + Object = 3, + Array = 4, + Binary = 5, + Undefined = 6, + Oid = 7, + Boolean = 8, + Date = 9, + Null = 10, + Regex = 11, + Reference = 12, + Code = 13, + Symbol = 14, + CodeWScope = 15, + Integer = 16, + TimeStamp = 17, + Long = 18, + MinKey = -1, + MaxKey = 127 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Bson/BsonWriter.cs b/Libs/JsonNet/Bson/BsonWriter.cs new file mode 100644 index 0000000..a0ea088 --- /dev/null +++ b/Libs/JsonNet/Bson/BsonWriter.cs @@ -0,0 +1,517 @@ +#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; +using System.Collections.Generic; +using System.IO; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using System.Text; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. + /// + public class BsonWriter : JsonWriter + { + private readonly BsonBinaryWriter _writer; + + private BsonToken _root; + private BsonToken _parent; + private string _propertyName; + + /// + /// Gets or sets the used when writing values to BSON. + /// When set to no conversion will occur. + /// + /// The used when writing values to BSON. + public DateTimeKind DateTimeKindHandling + { + get { return _writer.DateTimeKindHandling; } + set { _writer.DateTimeKindHandling = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public BsonWriter(Stream stream) + { + ValidationUtils.ArgumentNotNull(stream, "stream"); + _writer = new BsonBinaryWriter(new BinaryWriter(stream)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The writer. + public BsonWriter(BinaryWriter writer) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + _writer = new BsonBinaryWriter(writer); + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + _writer.Flush(); + } + + /// + /// Writes the end. + /// + /// The token. + protected override void WriteEnd(JsonToken token) + { + base.WriteEnd(token); + RemoveParent(); + + if (Top == 0) + { + _writer.WriteToken(_root); + } + } + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + throw JsonWriterException.Create(this, "Cannot write JSON comment as BSON.", null); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + throw JsonWriterException.Create(this, "Cannot write JSON constructor as BSON.", null); + } + + /// + /// Writes raw JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + throw JsonWriterException.Create(this, "Cannot write raw JSON as BSON.", null); + } + + /// + /// Writes raw JSON where a value is expected and updates the writer's state. + /// + /// The raw JSON to write. + public override void WriteRawValue(string json) + { + throw JsonWriterException.Create(this, "Cannot write raw JSON as BSON.", null); + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + AddParent(new BsonArray()); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + AddParent(new BsonObject()); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + _propertyName = name; + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + + if (CloseOutput && _writer != null) + _writer.Close(); + } + + private void AddParent(BsonToken container) + { + AddToken(container); + _parent = container; + } + + private void RemoveParent() + { + _parent = _parent.Parent; + } + + private void AddValue(object value, BsonType type) + { + AddToken(new BsonValue(value, type)); + } + + internal void AddToken(BsonToken token) + { + if (_parent != null) + { + if (_parent is BsonObject) + { + ((BsonObject)_parent).Add(_propertyName, token); + _propertyName = null; + } + else + { + ((BsonArray)_parent).Add(token); + } + } + else + { + if (token.Type != BsonType.Object && token.Type != BsonType.Array) + throw JsonWriterException.Create(this, "Error writing {0} value. BSON must start with an Object or Array.".FormatWith(CultureInfo.InvariantCulture, token.Type), null); + + _parent = token; + _root = token; + } + } + + #region WriteValue methods + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public override void WriteValue(object value) + { +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + InternalWriteValue(JsonToken.Integer); + AddToken(new BsonBinary(((BigInteger)value).ToByteArray(), BsonBinaryType.Binary)); + } + else +#endif + { + base.WriteValue(value); + } + } + + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + AddValue(null, BsonType.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + AddValue(null, BsonType.Undefined); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + if (value == null) + AddValue(null, BsonType.Null); + else + AddToken(new BsonString(value, true)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(uint value) + { + if (value > int.MaxValue) + throw JsonWriterException.Create(this, "Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values.", null); + + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + AddValue(value, BsonType.Long); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ulong value) + { + if (value > long.MaxValue) + throw JsonWriterException.Create(this, "Value is too large to fit in a signed 64 bit integer. BSON does not support unsigned values.", null); + + base.WriteValue(value); + AddValue(value, BsonType.Long); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + AddValue(value, BsonType.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ushort value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + string s = null; +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + s = value.ToString(CultureInfo.InvariantCulture); +#else + s = value.ToString(); +#endif + AddToken(new BsonString(s, true)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling); + AddValue(value, BsonType.Date); + } + +#if !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + AddValue(value, BsonType.Date); + } +#endif + + /// + /// Writes a [] value. + /// + /// The [] value to write. + public override void WriteValue(byte[] value) + { + base.WriteValue(value); + AddToken(new BsonBinary(value, BsonBinaryType.Binary)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Guid value) + { + base.WriteValue(value); + AddToken(new BsonBinary(value.ToByteArray(), BsonBinaryType.Uuid)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(TimeSpan value) + { + base.WriteValue(value); + AddToken(new BsonString(value.ToString(), true)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Uri value) + { + base.WriteValue(value); + AddToken(new BsonString(value.ToString(), true)); + } + #endregion + + /// + /// Writes a [] value that represents a BSON object id. + /// + /// The Object ID value to write. + public void WriteObjectId(byte[] value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value.Length != 12) + throw JsonWriterException.Create(this, "An object id must be 12 bytes", null); + + // hack to update the writer state + UpdateScopeWithFinishedValue(); + AutoComplete(JsonToken.Undefined); + AddValue(value, BsonType.Oid); + } + + /// + /// Writes a BSON regex. + /// + /// The regex pattern. + /// The regex options. + public void WriteRegex(string pattern, string options) + { + ValidationUtils.ArgumentNotNull(pattern, "pattern"); + + // hack to update the writer state + UpdateScopeWithFinishedValue(); + AutoComplete(JsonToken.Undefined); + AddToken(new BsonRegex(pattern, options)); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/ConstructorHandling.cs b/Libs/JsonNet/ConstructorHandling.cs new file mode 100644 index 0000000..e403f1e --- /dev/null +++ b/Libs/JsonNet/ConstructorHandling.cs @@ -0,0 +1,43 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how constructors are used when initializing objects during deserialization by the . + /// + public enum ConstructorHandling + { + /// + /// First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor. + /// + Default = 0, + + /// + /// Json.NET will use a non-public default constructor before falling back to a paramatized constructor. + /// + AllowNonPublicDefaultConstructor = 1 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/BinaryConverter.cs b/Libs/JsonNet/Converters/BinaryConverter.cs new file mode 100644 index 0000000..6c2284d --- /dev/null +++ b/Libs/JsonNet/Converters/BinaryConverter.cs @@ -0,0 +1,188 @@ +#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 !(NETFX_CORE || PORTABLE40 || PORTABLE) +using System; +using System.Data.SqlTypes; +using System.Globalization; +using Newtonsoft.Json.Utilities; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a binary value to and from a base 64 string value. + /// + public class BinaryConverter : JsonConverter + { +#if !NET20 + private const string BinaryTypeName = "System.Data.Linq.Binary"; + private const string BinaryToArrayName = "ToArray"; + private ReflectionObject _reflectionObject; +#endif + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + byte[] data = GetByteArray(value); + + writer.WriteValue(data); + } + + private byte[] GetByteArray(object value) + { +#if !(NET20) + if (value.GetType().AssignableToTypeName(BinaryTypeName)) + { + EnsureReflectionObject(value.GetType()); + return (byte[])_reflectionObject.GetValue(value, BinaryToArrayName); + } +#endif + if (value is SqlBinary) + return ((SqlBinary)value).Value; + + throw new JsonSerializationException("Unexpected value type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + +#if !NET20 + private void EnsureReflectionObject(Type t) + { + if (_reflectionObject == null) + _reflectionObject = ReflectionObject.Create(t, t.GetConstructor(new[] { typeof(byte[]) }), BinaryToArrayName); + } +#endif + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullable(objectType)) + throw JsonSerializationException.Create(reader, "Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + byte[] data; + + if (reader.TokenType == JsonToken.StartArray) + { + data = ReadByteArray(reader); + } + else if (reader.TokenType == JsonToken.String) + { + // current token is already at base64 string + // unable to call ReadAsBytes so do it the old fashion way + string encodedData = reader.Value.ToString(); + data = Convert.FromBase64String(encodedData); + } + else + { + throw JsonSerializationException.Create(reader, "Unexpected token parsing binary. Expected String or StartArray, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + +#if !NET20 + if (t.AssignableToTypeName(BinaryTypeName)) + { + EnsureReflectionObject(t); + + return _reflectionObject.Creator(data); + } +#endif + + if (t == typeof(SqlBinary)) + return new SqlBinary(data); + + throw JsonSerializationException.Create(reader, "Unexpected object type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + + private byte[] ReadByteArray(JsonReader reader) + { + List byteList = new List(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Integer: + byteList.Add(Convert.ToByte(reader.Value, CultureInfo.InvariantCulture)); + break; + case JsonToken.EndArray: + return byteList.ToArray(); + case JsonToken.Comment: + // skip + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when reading bytes: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + throw JsonSerializationException.Create(reader, "Unexpected end when reading bytes."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { +#if !NET20 + if (objectType.AssignableToTypeName(BinaryTypeName)) + return true; +#endif + + if (objectType == typeof(SqlBinary) || objectType == typeof(SqlBinary?)) + return true; + + return false; + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/BsonObjectIdConverter.cs b/Libs/JsonNet/Converters/BsonObjectIdConverter.cs new file mode 100644 index 0000000..8c5618d --- /dev/null +++ b/Libs/JsonNet/Converters/BsonObjectIdConverter.cs @@ -0,0 +1,89 @@ +#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 Newtonsoft.Json.Bson; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON and BSON. + /// + public class BsonObjectIdConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + BsonObjectId objectId = (BsonObjectId)value; + + BsonWriter bsonWriter = writer as BsonWriter; + if (bsonWriter != null) + { + bsonWriter.WriteObjectId(objectId.Value); + } + else + { + writer.WriteValue(objectId.Value); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.Bytes) + throw new JsonSerializationException("Expected Bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + byte[] value = (byte[])reader.Value; + + return new BsonObjectId(value); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(BsonObjectId)); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/CustomCreationConverter.cs b/Libs/JsonNet/Converters/CustomCreationConverter.cs new file mode 100644 index 0000000..adf519b --- /dev/null +++ b/Libs/JsonNet/Converters/CustomCreationConverter.cs @@ -0,0 +1,99 @@ +#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 Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Create a custom object + /// + /// The object type to convert. + public abstract class CustomCreationConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotSupportedException("CustomCreationConverter should only be used while deserializing."); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + T value = Create(objectType); + if (value == null) + throw new JsonSerializationException("No object created."); + + serializer.Populate(reader, value); + return value; + } + + /// + /// Creates an object which will then be populated by the serializer. + /// + /// Type of the object. + /// The created object. + public abstract T Create(Type objectType); + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return typeof(T).IsAssignableFrom(objectType); + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/DataSetConverter.cs b/Libs/JsonNet/Converters/DataSetConverter.cs new file mode 100644 index 0000000..3bfffff --- /dev/null +++ b/Libs/JsonNet/Converters/DataSetConverter.cs @@ -0,0 +1,118 @@ +#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 !(NETFX_CORE || PORTABLE40 || PORTABLE) +using System; +using System.Data; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class DataSetConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataSet dataSet = (DataSet)value; + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + DataTableConverter converter = new DataTableConverter(); + + writer.WriteStartObject(); + + foreach (DataTable table in dataSet.Tables) + { + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(table.TableName) : table.TableName); + + converter.WriteJson(writer, table, serializer); + } + + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + // handle typed datasets + DataSet ds = (objectType == typeof(DataSet)) + ? new DataSet() + : (DataSet)Activator.CreateInstance(objectType); + + DataTableConverter converter = new DataTableConverter(); + + CheckedRead(reader); + + while (reader.TokenType == JsonToken.PropertyName) + { + DataTable dt = ds.Tables[(string)reader.Value]; + bool exists = (dt != null); + + dt = (DataTable)converter.ReadJson(reader, typeof(DataTable), dt, serializer); + + if (!exists) + ds.Tables.Add(dt); + + CheckedRead(reader); + } + + return ds; + } + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { + return typeof(DataSet).IsAssignableFrom(valueType); + } + + private void CheckedRead(JsonReader reader) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading DataSet."); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/DataTableConverter.cs b/Libs/JsonNet/Converters/DataTableConverter.cs new file mode 100644 index 0000000..82d3143 --- /dev/null +++ b/Libs/JsonNet/Converters/DataTableConverter.cs @@ -0,0 +1,226 @@ +#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.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) +using System; +using System.Data; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class DataTableConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataTable table = (DataTable)value; + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + writer.WriteStartArray(); + + foreach (DataRow row in table.Rows) + { + writer.WriteStartObject(); + foreach (DataColumn column in row.Table.Columns) + { + if (serializer.NullValueHandling == NullValueHandling.Ignore && (row[column] == null || row[column] == DBNull.Value)) + continue; + + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(column.ColumnName) : column.ColumnName); + serializer.Serialize(writer, row[column]); + } + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + DataTable dt = existingValue as DataTable; + + if (dt == null) + { + // handle typed datasets + dt = (objectType == typeof(DataTable)) + ? new DataTable() + : (DataTable)Activator.CreateInstance(objectType); + } + + if (reader.TokenType == JsonToken.PropertyName) + { + dt.TableName = (string)reader.Value; + + CheckedRead(reader); + } + + if (reader.TokenType != JsonToken.StartArray) + throw JsonSerializationException.Create(reader, "Unexpected JSON token when reading DataTable. Expected StartArray, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + CheckedRead(reader); + + while (reader.TokenType != JsonToken.EndArray) + { + CreateRow(reader, dt); + + CheckedRead(reader); + } + + return dt; + } + + private static void CreateRow(JsonReader reader, DataTable dt) + { + DataRow dr = dt.NewRow(); + CheckedRead(reader); + + while (reader.TokenType == JsonToken.PropertyName) + { + string columnName = (string)reader.Value; + + CheckedRead(reader); + + DataColumn column = dt.Columns[columnName]; + if (column == null) + { + Type columnType = GetColumnDataType(reader); + column = new DataColumn(columnName, columnType); + dt.Columns.Add(column); + } + + if (column.DataType == typeof(DataTable)) + { + if (reader.TokenType == JsonToken.StartArray) + CheckedRead(reader); + + DataTable nestedDt = new DataTable(); + + while (reader.TokenType != JsonToken.EndArray) + { + CreateRow(reader, nestedDt); + + CheckedRead(reader); + } + + dr[columnName] = nestedDt; + } + else if (column.DataType.IsArray && column.DataType != typeof(byte[])) + { + if (reader.TokenType == JsonToken.StartArray) + CheckedRead(reader); + + List o = new List(); + + while (reader.TokenType != JsonToken.EndArray) + { + o.Add(reader.Value); + CheckedRead(reader); + } + + Array destinationArray = Array.CreateInstance(column.DataType.GetElementType(), o.Count); + Array.Copy(o.ToArray(), destinationArray, o.Count); + + dr[columnName] = destinationArray; + } + else + { + dr[columnName] = reader.Value ?? DBNull.Value; + } + + CheckedRead(reader); + } + + dr.EndEdit(); + dt.Rows.Add(dr); + } + + private static Type GetColumnDataType(JsonReader reader) + { + JsonToken tokenType = reader.TokenType; + + switch (tokenType) + { + case JsonToken.Integer: + case JsonToken.Boolean: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Date: + case JsonToken.Bytes: + return reader.ValueType; + case JsonToken.Null: + case JsonToken.Undefined: + return typeof(string); + case JsonToken.StartArray: + CheckedRead(reader); + if (reader.TokenType == JsonToken.StartObject) + return typeof(DataTable); // nested datatable + + Type arrayType = GetColumnDataType(reader); + return arrayType.MakeArrayType(); + default: + throw JsonSerializationException.Create(reader, "Unexpected JSON token when reading DataTable: {0}".FormatWith(CultureInfo.InvariantCulture, tokenType)); + } + } + + private static void CheckedRead(JsonReader reader) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading DataTable."); + } + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { + return typeof(DataTable).IsAssignableFrom(valueType); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/DateTimeConverterBase.cs b/Libs/JsonNet/Converters/DateTimeConverterBase.cs new file mode 100644 index 0000000..a928b13 --- /dev/null +++ b/Libs/JsonNet/Converters/DateTimeConverterBase.cs @@ -0,0 +1,54 @@ +#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; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Provides a base class for converting a to and from JSON. + /// + public abstract class DateTimeConverterBase : JsonConverter + { + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) + return true; +#if !NET20 + if (objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?)) + return true; +#endif + + return false; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/DiscriminatedUnionConverter.cs b/Libs/JsonNet/Converters/DiscriminatedUnionConverter.cs new file mode 100644 index 0000000..0edb0f9 --- /dev/null +++ b/Libs/JsonNet/Converters/DiscriminatedUnionConverter.cs @@ -0,0 +1,214 @@ +#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 !(NET35 || NET20 || NETFX_CORE) +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using System.Reflection; +using Newtonsoft.Json.Serialization; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a F# discriminated union type to and from JSON. + /// + public class DiscriminatedUnionConverter : JsonConverter + { + private const string CasePropertyName = "Case"; + private const string FieldsPropertyName = "Fields"; + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + Type t = value.GetType(); + + object result = FSharpUtils.GetUnionFields(null, value, t, null); + object info = FSharpUtils.GetUnionCaseInfo(result); + object fields = FSharpUtils.GetUnionCaseFields(result); + object caseName = FSharpUtils.GetUnionCaseInfoName(info); + object[] fieldsAsArray = fields as object[]; + + writer.WriteStartObject(); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(CasePropertyName) : CasePropertyName); + writer.WriteValue((string)caseName); + if (fieldsAsArray != null && fieldsAsArray.Length > 0) + { + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(FieldsPropertyName) : FieldsPropertyName); + serializer.Serialize(writer, fields); + } + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + object matchingCaseInfo = null; + string caseName = null; + JArray fields = null; + + // start object + ReadAndAssert(reader); + + while (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = reader.Value.ToString(); + if (string.Equals(propertyName, CasePropertyName, StringComparison.OrdinalIgnoreCase)) + { + ReadAndAssert(reader); + + IEnumerable cases = (IEnumerable)FSharpUtils.GetUnionCases(null, objectType, null); + + caseName = reader.Value.ToString(); + + foreach (object c in cases) + { + if ((string)FSharpUtils.GetUnionCaseInfoName(c) == caseName) + { + matchingCaseInfo = c; + break; + } + } + + if (matchingCaseInfo == null) + throw JsonSerializationException.Create(reader, "No union type found with the name '{0}'.".FormatWith(CultureInfo.InvariantCulture, caseName)); + } + else if (string.Equals(propertyName, FieldsPropertyName, StringComparison.OrdinalIgnoreCase)) + { + ReadAndAssert(reader); + if (reader.TokenType != JsonToken.StartArray) + throw JsonSerializationException.Create(reader, "Union fields must been an array."); + + fields = (JArray)JToken.ReadFrom(reader); + } + else + { + throw JsonSerializationException.Create(reader, "Unexpected property '{0}' found when reading union.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + + ReadAndAssert(reader); + } + + if (matchingCaseInfo == null) + throw JsonSerializationException.Create(reader, "No '{0}' property with union name found.".FormatWith(CultureInfo.InvariantCulture, CasePropertyName)); + + PropertyInfo[] fieldProperties = (PropertyInfo[])FSharpUtils.GetUnionCaseInfoFields(matchingCaseInfo); + object[] typedFieldValues = new object[fieldProperties.Length]; + + if (fieldProperties.Length > 0 && fields == null) + throw JsonSerializationException.Create(reader, "No '{0}' property with union fields found.".FormatWith(CultureInfo.InvariantCulture, FieldsPropertyName)); + + if (fields != null) + { + if (fieldProperties.Length != fields.Count) + throw JsonSerializationException.Create(reader, "The number of field values does not match the number of properties definied by union '{0}'.".FormatWith(CultureInfo.InvariantCulture, caseName)); + + + for (int i = 0; i < fields.Count; i++) + { + JToken t = fields[i]; + PropertyInfo fieldProperty = fieldProperties[i]; + + typedFieldValues[i] = t.ToObject(fieldProperty.PropertyType, serializer); + } + } + + return FSharpUtils.MakeUnion(null, matchingCaseInfo, typedFieldValues, null); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + if (typeof(IEnumerable).IsAssignableFrom(objectType)) + return false; + + // all fsharp objects have CompilationMappingAttribute + // get the fsharp assembly from the attribute and initialize latebound methods + object[] attributes; +#if !(NETFX_CORE || PORTABLE) + attributes = objectType.GetCustomAttributes(true); +#else + attributes = objectType.GetTypeInfo().GetCustomAttributes(true).ToArray(); +#endif + + bool isFSharpType = false; + foreach (object attribute in attributes) + { + Type attributeType = attribute.GetType(); + if (attributeType.FullName == "Microsoft.FSharp.Core.CompilationMappingAttribute") + { + FSharpUtils.EnsureInitialized(attributeType.Assembly()); + + isFSharpType = true; + break; + } + } + + if (!isFSharpType) + return false; + + return (bool)FSharpUtils.IsUnion(null, objectType, null); + } + + private static void ReadAndAssert(JsonReader reader) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading union."); + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/EntityKeyMemberConverter.cs b/Libs/JsonNet/Converters/EntityKeyMemberConverter.cs new file mode 100644 index 0000000..d3b1075 --- /dev/null +++ b/Libs/JsonNet/Converters/EntityKeyMemberConverter.cs @@ -0,0 +1,154 @@ +#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 !(NET20 || NETFX_CORE || PORTABLE40 || PORTABLE) +using System; +using Newtonsoft.Json.Serialization; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an Entity Framework EntityKey to and from JSON. + /// + public class EntityKeyMemberConverter : JsonConverter + { + private const string EntityKeyMemberFullTypeName = "System.Data.EntityKeyMember"; + + private const string KeyPropertyName = "Key"; + private const string TypePropertyName = "Type"; + private const string ValuePropertyName = "Value"; + + private static ReflectionObject _reflectionObject; + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + EnsureReflectionObject(value.GetType()); + + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + string keyName = (string)_reflectionObject.GetValue(value, KeyPropertyName); + object keyValue = _reflectionObject.GetValue(value, ValuePropertyName); + + Type keyValueType = (keyValue != null) ? keyValue.GetType() : null; + + writer.WriteStartObject(); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(KeyPropertyName) : KeyPropertyName); + writer.WriteValue(keyName); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(TypePropertyName) : TypePropertyName); + writer.WriteValue((keyValueType != null) ? keyValueType.FullName : null); + + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(ValuePropertyName) : ValuePropertyName); + + if (keyValueType != null) + { + string valueJson; + if (JsonSerializerInternalWriter.TryConvertToString(keyValue, keyValueType, out valueJson)) + writer.WriteValue(valueJson); + else + writer.WriteValue(keyValue); + } + else + { + writer.WriteNull(); + } + + writer.WriteEndObject(); + } + + private static void ReadAndAssertProperty(JsonReader reader, string propertyName) + { + ReadAndAssert(reader); + + if (reader.TokenType != JsonToken.PropertyName || !string.Equals(reader.Value.ToString(), propertyName, StringComparison.OrdinalIgnoreCase)) + throw new JsonSerializationException("Expected JSON property '{0}'.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + + private static void ReadAndAssert(JsonReader reader) + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end."); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + EnsureReflectionObject(objectType); + + object entityKeyMember = _reflectionObject.Creator(); + + ReadAndAssertProperty(reader, KeyPropertyName); + ReadAndAssert(reader); + _reflectionObject.SetValue(entityKeyMember, KeyPropertyName, reader.Value.ToString()); + + ReadAndAssertProperty(reader, TypePropertyName); + ReadAndAssert(reader); + string type = reader.Value.ToString(); + + Type t = Type.GetType(type); + + ReadAndAssertProperty(reader, ValuePropertyName); + ReadAndAssert(reader); + _reflectionObject.SetValue(entityKeyMember, ValuePropertyName, serializer.Deserialize(reader, t)); + + ReadAndAssert(reader); + + return entityKeyMember; + } + + private static void EnsureReflectionObject(Type objectType) + { + if (_reflectionObject == null) + _reflectionObject = ReflectionObject.Create(objectType, KeyPropertyName, ValuePropertyName); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return objectType.AssignableToTypeName(EntityKeyMemberFullTypeName); + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/ExpandoObjectConverter.cs b/Libs/JsonNet/Converters/ExpandoObjectConverter.cs new file mode 100644 index 0000000..d07599d --- /dev/null +++ b/Libs/JsonNet/Converters/ExpandoObjectConverter.cs @@ -0,0 +1,165 @@ +#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 !(NET35 || NET20 || PORTABLE40) + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an ExpandoObject to and from JSON. + /// + public class ExpandoObjectConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + // can write is set to false + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return ReadValue(reader); + } + + private object ReadValue(JsonReader reader) + { + while (reader.TokenType == JsonToken.Comment) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading ExpandoObject."); + } + + switch (reader.TokenType) + { + case JsonToken.StartObject: + return ReadObject(reader); + case JsonToken.StartArray: + return ReadList(reader); + default: + if (JsonReader.IsPrimitiveToken(reader.TokenType)) + return reader.Value; + + throw JsonSerializationException.Create(reader, "Unexpected token when converting ExpandoObject: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private object ReadList(JsonReader reader) + { + IList list = new List(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + break; + default: + object v = ReadValue(reader); + + list.Add(v); + break; + case JsonToken.EndArray: + return list; + } + } + + throw JsonSerializationException.Create(reader, "Unexpected end when reading ExpandoObject."); + } + + private object ReadObject(JsonReader reader) + { + IDictionary expandoObject = new ExpandoObject(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string propertyName = reader.Value.ToString(); + + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading ExpandoObject."); + + object v = ReadValue(reader); + + expandoObject[propertyName] = v; + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + return expandoObject; + } + } + + throw JsonSerializationException.Create(reader, "Unexpected end when reading ExpandoObject."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(ExpandoObject)); + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/IsoDateTimeConverter.cs b/Libs/JsonNet/Converters/IsoDateTimeConverter.cs new file mode 100644 index 0000000..a7d78e8 --- /dev/null +++ b/Libs/JsonNet/Converters/IsoDateTimeConverter.cs @@ -0,0 +1,171 @@ +#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.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z). + /// + public class IsoDateTimeConverter : DateTimeConverterBase + { + private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; + + private DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind; + private string _dateTimeFormat; + private CultureInfo _culture; + + /// + /// Gets or sets the date time styles used when converting a date to and from JSON. + /// + /// The date time styles used when converting a date to and from JSON. + public DateTimeStyles DateTimeStyles + { + get { return _dateTimeStyles; } + set { _dateTimeStyles = value; } + } + + /// + /// Gets or sets the date time format used when converting a date to and from JSON. + /// + /// The date time format used when converting a date to and from JSON. + public string DateTimeFormat + { + get { return _dateTimeFormat ?? string.Empty; } + set { _dateTimeFormat = StringUtils.NullEmptyString(value); } + } + + /// + /// Gets or sets the culture used when converting a date to and from JSON. + /// + /// The culture used when converting a date to and from JSON. + public CultureInfo Culture + { + get { return _culture ?? CultureInfo.CurrentCulture; } + set { _culture = value; } + } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + string text; + + if (value is DateTime) + { + DateTime dateTime = (DateTime)value; + + if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal + || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) + dateTime = dateTime.ToUniversalTime(); + + text = dateTime.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture); + } +#if !NET20 + else if (value is DateTimeOffset) + { + DateTimeOffset dateTimeOffset = (DateTimeOffset)value; + if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal + || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) + dateTimeOffset = dateTimeOffset.ToUniversalTime(); + + text = dateTimeOffset.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture); + } +#endif + else + { + throw new JsonSerializationException("Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {0}.".FormatWith(CultureInfo.InvariantCulture, ReflectionUtils.GetObjectType(value))); + } + + writer.WriteValue(text); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + bool nullable = ReflectionUtils.IsNullableType(objectType); +#if !NET20 + Type t = (nullable) + ? Nullable.GetUnderlyingType(objectType) + : objectType; +#endif + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw JsonSerializationException.Create(reader, "Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType == JsonToken.Date) + { +#if !NET20 + if (t == typeof(DateTimeOffset)) + return reader.Value is DateTimeOffset ? reader.Value : new DateTimeOffset((DateTime)reader.Value); +#endif + + return reader.Value; + } + + if (reader.TokenType != JsonToken.String) + throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + string dateText = reader.Value.ToString(); + + if (string.IsNullOrEmpty(dateText) && nullable) + return null; + +#if !NET20 + if (t == typeof(DateTimeOffset)) + { + if (!string.IsNullOrEmpty(_dateTimeFormat)) + return DateTimeOffset.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles); + else + return DateTimeOffset.Parse(dateText, Culture, _dateTimeStyles); + } +#endif + + if (!string.IsNullOrEmpty(_dateTimeFormat)) + return DateTime.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles); + else + return DateTime.Parse(dateText, Culture, _dateTimeStyles); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/JavaScriptDateTimeConverter.cs b/Libs/JsonNet/Converters/JavaScriptDateTimeConverter.cs new file mode 100644 index 0000000..d670279 --- /dev/null +++ b/Libs/JsonNet/Converters/JavaScriptDateTimeConverter.cs @@ -0,0 +1,120 @@ +#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.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from a JavaScript date constructor (e.g. new Date(52231943)). + /// + public class JavaScriptDateTimeConverter : DateTimeConverterBase + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + long ticks; + + if (value is DateTime) + { + DateTime dateTime = (DateTime)value; + DateTime utcDateTime = dateTime.ToUniversalTime(); + ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(utcDateTime); + } +#if !NET20 + else if (value is DateTimeOffset) + { + DateTimeOffset dateTimeOffset = (DateTimeOffset)value; + DateTimeOffset utcDateTimeOffset = dateTimeOffset.ToUniversalTime(); + ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(utcDateTimeOffset.UtcDateTime); + } +#endif + else + { + throw new JsonSerializationException("Expected date object value."); + } + + writer.WriteStartConstructor("Date"); + writer.WriteValue(ticks); + writer.WriteEndConstructor(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing property value of the JSON that is being converted. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { +#if !NET20 + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; +#endif + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullable(objectType)) + throw JsonSerializationException.Create(reader, "Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType != JsonToken.StartConstructor || !string.Equals(reader.Value.ToString(), "Date", StringComparison.Ordinal)) + throw JsonSerializationException.Create(reader, "Unexpected token or value when parsing date. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType, reader.Value)); + + reader.Read(); + + if (reader.TokenType != JsonToken.Integer) + throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + long ticks = (long)reader.Value; + + DateTime d = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ticks); + + reader.Read(); + + if (reader.TokenType != JsonToken.EndConstructor) + throw JsonSerializationException.Create(reader, "Unexpected token parsing date. Expected EndConstructor, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + +#if !NET20 + if (t == typeof(DateTimeOffset)) + return new DateTimeOffset(d); +#endif + + return d; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/JsonValueConverter.cs b/Libs/JsonNet/Converters/JsonValueConverter.cs new file mode 100644 index 0000000..25f979f --- /dev/null +++ b/Libs/JsonNet/Converters/JsonValueConverter.cs @@ -0,0 +1,225 @@ +#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 NETFX_CORE +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using Newtonsoft.Json.Utilities; +using Windows.Data.Json; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class JsonValueConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + WriteJsonValue(writer, (IJsonValue)value); + } + + private void WriteJsonValue(JsonWriter writer, IJsonValue value) + { + switch (value.ValueType) + { + case JsonValueType.Array: + { + JsonArray a = value.GetArray(); + writer.WriteStartArray(); + for (int i = 0; i < a.Count; i++) + { + WriteJsonValue(writer, a[i]); + } + writer.WriteEndArray(); + } + break; + case JsonValueType.Boolean: + { + writer.WriteValue(value.GetBoolean()); + } + break; + case JsonValueType.Null: + { + writer.WriteNull(); + } + break; + case JsonValueType.Number: + { + // JsonValue doesn't support integers + // serialize whole numbers without a decimal point + double d = value.GetNumber(); + bool isInteger = (d % 1 == 0); + if (isInteger && d <= long.MaxValue && d >= long.MinValue) + writer.WriteValue(Convert.ToInt64(d)); + else + writer.WriteValue(d); + } + break; + case JsonValueType.Object: + { + JsonObject o = value.GetObject(); + writer.WriteStartObject(); + foreach (KeyValuePair v in o) + { + writer.WritePropertyName(v.Key); + WriteJsonValue(writer, v.Value); + } + writer.WriteEndObject(); + } + break; + case JsonValueType.String: + { + writer.WriteValue(value.GetString()); + } + break; + default: + throw new ArgumentOutOfRangeException("ValueType"); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.None) + reader.Read(); + + IJsonValue value = CreateJsonValue(reader); + + if (!objectType.IsAssignableFrom(value.GetType())) + throw JsonSerializationException.Create(reader, "Could not convert '{0}' to '{1}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), objectType)); + + return value; + } + + private IJsonValue CreateJsonValue(JsonReader reader) + { + while (reader.TokenType == JsonToken.Comment) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end."); + } + + switch (reader.TokenType) + { + case JsonToken.StartObject: + { + return CreateJsonObject(reader); + } + case JsonToken.StartArray: + { + JsonArray a = new JsonArray(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.EndArray: + return a; + default: + IJsonValue value = CreateJsonValue(reader); + a.Add(value); + break; + } + } + } + break; + case JsonToken.Integer: + case JsonToken.Float: + return JsonValue.CreateNumberValue(Convert.ToDouble(reader.Value, CultureInfo.InvariantCulture)); + case JsonToken.String: + return JsonValue.CreateStringValue(reader.Value.ToString()); + case JsonToken.Boolean: + return JsonValue.CreateBooleanValue(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); + case JsonToken.Null: + // surely there is a better way to create a null value than this? + return JsonValue.Parse("null"); + case JsonToken.Date: + return JsonValue.CreateStringValue(reader.Value.ToString()); + case JsonToken.Bytes: + return JsonValue.CreateStringValue(reader.Value.ToString()); + default: + throw JsonSerializationException.Create(reader, "Unexpected or unsupported token: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + throw JsonSerializationException.Create(reader, "Unexpected end."); + } + + private JsonObject CreateJsonObject(JsonReader reader) + { + JsonObject o = new JsonObject(); + string propertyName = null; + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + propertyName = (string)reader.Value; + break; + case JsonToken.EndObject: + return o; + case JsonToken.Comment: + break; + default: + IJsonValue propertyValue = CreateJsonValue(reader); + o.Add(propertyName, propertyValue); + break; + } + } + + throw JsonSerializationException.Create(reader, "Unexpected end."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return typeof(IJsonValue).IsAssignableFrom(objectType); + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Converters/KeyValuePairConverter.cs b/Libs/JsonNet/Converters/KeyValuePairConverter.cs new file mode 100644 index 0000000..e5b219f --- /dev/null +++ b/Libs/JsonNet/Converters/KeyValuePairConverter.cs @@ -0,0 +1,153 @@ +#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 Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using System.Reflection; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class KeyValuePairConverter : JsonConverter + { + private const string KeyName = "Key"; + private const string ValueName = "Value"; + + private static readonly ThreadSafeStore ReflectionObjectPerType = new ThreadSafeStore(InitializeReflectionObject); + + private static ReflectionObject InitializeReflectionObject(Type t) + { + IList genericArguments = t.GetGenericArguments(); + Type keyType = genericArguments[0]; + Type valueType = genericArguments[1]; + + return ReflectionObject.Create(t, t.GetConstructor(new[] { keyType, valueType }), KeyName, ValueName); + } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + ReflectionObject reflectionObject = ReflectionObjectPerType.Get(value.GetType()); + + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + writer.WriteStartObject(); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(KeyName) : KeyName); + serializer.Serialize(writer, reflectionObject.GetValue(value, KeyName), reflectionObject.GetType(KeyName)); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(ValueName) : ValueName); + serializer.Serialize(writer, reflectionObject.GetValue(value, ValueName), reflectionObject.GetType(ValueName)); + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + bool isNullable = ReflectionUtils.IsNullableType(objectType); + + Type t = (isNullable) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + ReflectionObject reflectionObject = ReflectionObjectPerType.Get(t); + + if (reader.TokenType == JsonToken.Null) + { + if (!isNullable) + throw JsonSerializationException.Create(reader, "Cannot convert null value to KeyValuePair."); + + return null; + } + + object key = null; + object value = null; + + ReadAndAssert(reader); + + while (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = reader.Value.ToString(); + if (string.Equals(propertyName, KeyName, StringComparison.OrdinalIgnoreCase)) + { + ReadAndAssert(reader); + key = serializer.Deserialize(reader, reflectionObject.GetType(KeyName)); + } + else if (string.Equals(propertyName, ValueName, StringComparison.OrdinalIgnoreCase)) + { + ReadAndAssert(reader); + value = serializer.Deserialize(reader, reflectionObject.GetType(ValueName)); + } + else + { + reader.Skip(); + } + + ReadAndAssert(reader); + } + + return reflectionObject.Creator(key, value); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (t.IsValueType() && t.IsGenericType()) + return (t.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)); + + return false; + } + + private static void ReadAndAssert(JsonReader reader) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading KeyValuePair."); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/RegexConverter.cs b/Libs/JsonNet/Converters/RegexConverter.cs new file mode 100644 index 0000000..e589833 --- /dev/null +++ b/Libs/JsonNet/Converters/RegexConverter.cs @@ -0,0 +1,201 @@ +#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.Text.RegularExpressions; +using Newtonsoft.Json.Bson; +using System.Globalization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON and BSON. + /// + public class RegexConverter : JsonConverter + { + private const string PatternName = "Pattern"; + private const string OptionsName = "Options"; + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Regex regex = (Regex)value; + + BsonWriter bsonWriter = writer as BsonWriter; + if (bsonWriter != null) + WriteBson(bsonWriter, regex); + else + WriteJson(writer, regex, serializer); + } + + private bool HasFlag(RegexOptions options, RegexOptions flag) + { + return ((options & flag) == flag); + } + + private void WriteBson(BsonWriter writer, Regex regex) + { + // Regular expression - The first cstring is the regex pattern, the second + // is the regex options string. Options are identified by characters, which + // must be stored in alphabetical order. Valid options are 'i' for case + // insensitive matching, 'm' for multiline matching, 'x' for verbose mode, + // 'l' to make \w, \W, etc. locale dependent, 's' for dotall mode + // ('.' matches everything), and 'u' to make \w, \W, etc. match unicode. + + string options = null; + + if (HasFlag(regex.Options, RegexOptions.IgnoreCase)) + options += "i"; + + if (HasFlag(regex.Options, RegexOptions.Multiline)) + options += "m"; + + if (HasFlag(regex.Options, RegexOptions.Singleline)) + options += "s"; + + options += "u"; + + if (HasFlag(regex.Options, RegexOptions.ExplicitCapture)) + options += "x"; + + writer.WriteRegex(regex.ToString(), options); + } + + private void WriteJson(JsonWriter writer, Regex regex, JsonSerializer serializer) + { + DefaultContractResolver resolver = serializer.ContractResolver as DefaultContractResolver; + + writer.WriteStartObject(); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(PatternName) : PatternName); + writer.WriteValue(regex.ToString()); + writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(OptionsName) : OptionsName); + serializer.Serialize(writer, regex.Options); + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartObject) + return ReadRegexObject(reader, serializer); + + if (reader.TokenType == JsonToken.String) + return ReadRegexString(reader); + + throw JsonSerializationException.Create(reader, "Unexpected token when reading Regex."); + } + + private object ReadRegexString(JsonReader reader) + { + string regexText = (string)reader.Value; + int patternOptionDelimiterIndex = regexText.LastIndexOf('/'); + + string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1); + string optionsText = regexText.Substring(patternOptionDelimiterIndex + 1); + + RegexOptions options = RegexOptions.None; + foreach (char c in optionsText) + { + switch (c) + { + case 'i': + options |= RegexOptions.IgnoreCase; + break; + case 'm': + options |= RegexOptions.Multiline; + break; + case 's': + options |= RegexOptions.Singleline; + break; + case 'x': + options |= RegexOptions.ExplicitCapture; + break; + } + } + + return new Regex(patternText, options); + } + + private Regex ReadRegexObject(JsonReader reader, JsonSerializer serializer) + { + string pattern = null; + RegexOptions? options = null; + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string propertyName = reader.Value.ToString(); + + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when reading Regex."); + + if (string.Equals(propertyName, PatternName, StringComparison.OrdinalIgnoreCase)) + pattern = (string)reader.Value; + else if (string.Equals(propertyName, OptionsName, StringComparison.OrdinalIgnoreCase)) + options = serializer.Deserialize(reader); + else + reader.Skip(); + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + if (pattern == null) + throw JsonSerializationException.Create(reader, "Error deserializing Regex. No pattern found."); + + return new Regex(pattern, options ?? RegexOptions.None); + } + } + + throw JsonSerializationException.Create(reader, "Unexpected end when reading Regex."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(Regex)); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/StringEnumConverter.cs b/Libs/JsonNet/Converters/StringEnumConverter.cs new file mode 100644 index 0000000..a30304d --- /dev/null +++ b/Libs/JsonNet/Converters/StringEnumConverter.cs @@ -0,0 +1,160 @@ +#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.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an to and from its name string value. + /// + public class StringEnumConverter : JsonConverter + { + /// + /// Gets or sets a value indicating whether the written enum text should be camel case. + /// + /// true if the written enum text will be camel case; otherwise, false. + public bool CamelCaseText { get; set; } + + /// + /// Gets or sets a value indicating whether integer values are allowed. + /// + /// true if integers are allowed; otherwise, false. + public bool AllowIntegerValues { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public StringEnumConverter() + { + AllowIntegerValues = true; + } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + Enum e = (Enum)value; + + string enumName = e.ToString("G"); + + if (char.IsNumber(enumName[0]) || enumName[0] == '-') + { + // enum value has no name so write number + writer.WriteValue(value); + } + else + { + Type enumType = e.GetType(); + + string finalName = EnumUtils.ToEnumName(enumType, enumName, CamelCaseText); + + writer.WriteValue(finalName); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + bool isNullable = ReflectionUtils.IsNullableType(objectType); + Type t = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw JsonSerializationException.Create(reader, "Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + try + { + if (reader.TokenType == JsonToken.String) + { + string enumText = reader.Value.ToString(); + return EnumUtils.ParseEnumName(enumText, isNullable, t); + } + + if (reader.TokenType == JsonToken.Integer) + { + if (!AllowIntegerValues) + throw JsonSerializationException.Create(reader, "Integer value {0} is not allowed.".FormatWith(CultureInfo.InvariantCulture, reader.Value)); + + return ConvertUtils.ConvertOrCast(reader.Value, CultureInfo.InvariantCulture, t); + } + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.FormatValueForPrint(reader.Value), objectType), ex); + } + + // we don't actually expect to get here. + throw JsonSerializationException.Create(reader, "Unexpected token {0} when parsing enum.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + return t.IsEnum(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/VersionConverter.cs b/Libs/JsonNet/Converters/VersionConverter.cs new file mode 100644 index 0000000..3f512e4 --- /dev/null +++ b/Libs/JsonNet/Converters/VersionConverter.cs @@ -0,0 +1,106 @@ +#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.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from a string (e.g. "1.2.3.4"). + /// + public class VersionConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else if (value is Version) + { + writer.WriteValue(value.ToString()); + } + else + { + throw new JsonSerializationException("Expected Version object value"); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing property value of the JSON that is being converted. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + else + { + if (reader.TokenType == JsonToken.String) + { + try + { + Version v = new Version((string)reader.Value); + return v; + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error parsing version string: {0}".FormatWith(CultureInfo.InvariantCulture, reader.Value), ex); + } + } + else + { + throw JsonSerializationException.Create(reader, "Unexpected token or value when parsing version. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType, reader.Value)); + } + } + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Version); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Converters/XmlNodeConverter.cs b/Libs/JsonNet/Converters/XmlNodeConverter.cs new file mode 100644 index 0000000..402fb76 --- /dev/null +++ b/Libs/JsonNet/Converters/XmlNodeConverter.cs @@ -0,0 +1,1785 @@ +#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 !PORTABLE40 +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml; +#if !(NET20 || PORTABLE40) +using System.Xml.Linq; +#endif +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Converters +{ + #region XmlNodeWrappers +#if !NETFX_CORE && !PORTABLE && !PORTABLE40 + internal class XmlDocumentWrapper : XmlNodeWrapper, IXmlDocument + { + private readonly XmlDocument _document; + + public XmlDocumentWrapper(XmlDocument document) + : base(document) + { + _document = document; + } + + public IXmlNode CreateComment(string data) + { + return new XmlNodeWrapper(_document.CreateComment(data)); + } + + public IXmlNode CreateTextNode(string text) + { + return new XmlNodeWrapper(_document.CreateTextNode(text)); + } + + public IXmlNode CreateCDataSection(string data) + { + return new XmlNodeWrapper(_document.CreateCDataSection(data)); + } + + public IXmlNode CreateWhitespace(string text) + { + return new XmlNodeWrapper(_document.CreateWhitespace(text)); + } + + public IXmlNode CreateSignificantWhitespace(string text) + { + return new XmlNodeWrapper(_document.CreateSignificantWhitespace(text)); + } + + public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone) + { + return new XmlDeclarationWrapper(_document.CreateXmlDeclaration(version, encoding, standalone)); + } + + public IXmlNode CreateXmlDocumentType(string name, string publicId, string systemId, string internalSubset) + { + return new XmlDocumentTypeWrapper(_document.CreateDocumentType(name, publicId, systemId, null)); + } + + public IXmlNode CreateProcessingInstruction(string target, string data) + { + return new XmlNodeWrapper(_document.CreateProcessingInstruction(target, data)); + } + + public IXmlElement CreateElement(string elementName) + { + return new XmlElementWrapper(_document.CreateElement(elementName)); + } + + public IXmlElement CreateElement(string qualifiedName, string namespaceUri) + { + return new XmlElementWrapper(_document.CreateElement(qualifiedName, namespaceUri)); + } + + public IXmlNode CreateAttribute(string name, string value) + { + XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(name)); + attribute.Value = value; + + return attribute; + } + + public IXmlNode CreateAttribute(string qualifiedName, string namespaceUri, string value) + { + XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(qualifiedName, namespaceUri)); + attribute.Value = value; + + return attribute; + } + + public IXmlElement DocumentElement + { + get + { + if (_document.DocumentElement == null) + return null; + + return new XmlElementWrapper(_document.DocumentElement); + } + } + } + + internal class XmlElementWrapper : XmlNodeWrapper, IXmlElement + { + private readonly XmlElement _element; + + public XmlElementWrapper(XmlElement element) + : base(element) + { + _element = element; + } + + public void SetAttributeNode(IXmlNode attribute) + { + XmlNodeWrapper xmlAttributeWrapper = (XmlNodeWrapper)attribute; + + _element.SetAttributeNode((XmlAttribute)xmlAttributeWrapper.WrappedNode); + } + + public string GetPrefixOfNamespace(string namespaceUri) + { + return _element.GetPrefixOfNamespace(namespaceUri); + } + + public bool IsEmpty + { + get { return _element.IsEmpty; } + } + } + + internal class XmlDeclarationWrapper : XmlNodeWrapper, IXmlDeclaration + { + private readonly XmlDeclaration _declaration; + + public XmlDeclarationWrapper(XmlDeclaration declaration) + : base(declaration) + { + _declaration = declaration; + } + + public string Version + { + get { return _declaration.Version; } + } + + public string Encoding + { + get { return _declaration.Encoding; } + set { _declaration.Encoding = value; } + } + + public string Standalone + { + get { return _declaration.Standalone; } + set { _declaration.Standalone = value; } + } + } + + internal class XmlDocumentTypeWrapper : XmlNodeWrapper, IXmlDocumentType + { + private readonly XmlDocumentType _documentType; + + public XmlDocumentTypeWrapper(XmlDocumentType documentType) + : base(documentType) + { + _documentType = documentType; + } + + public string Name + { + get { return _documentType.Name; } + } + + public string System + { + get { return _documentType.SystemId; } + } + + public string Public + { + get { return _documentType.PublicId; } + } + + public string InternalSubset + { + get { return _documentType.InternalSubset; } + } + + public override string LocalName + { + get { return "DOCTYPE"; } + } + } + + internal class XmlNodeWrapper : IXmlNode + { + private readonly XmlNode _node; + private IList _childNodes; + + public XmlNodeWrapper(XmlNode node) + { + _node = node; + } + + public object WrappedNode + { + get { return _node; } + } + + public XmlNodeType NodeType + { + get { return _node.NodeType; } + } + + public virtual string LocalName + { + get { return _node.LocalName; } + } + + public IList ChildNodes + { + get + { + // childnodes is read multiple times + // cache results to prevent multiple reads which kills perf in large documents + if (_childNodes == null) + _childNodes = _node.ChildNodes.Cast().Select(WrapNode).ToList(); + + return _childNodes; + } + } + + internal static IXmlNode WrapNode(XmlNode node) + { + switch (node.NodeType) + { + case XmlNodeType.Element: + return new XmlElementWrapper((XmlElement) node); + case XmlNodeType.XmlDeclaration: + return new XmlDeclarationWrapper((XmlDeclaration) node); + case XmlNodeType.DocumentType: + return new XmlDocumentTypeWrapper((XmlDocumentType) node); + default: + return new XmlNodeWrapper(node); + } + } + + public IList Attributes + { + get + { + if (_node.Attributes == null) + return null; + + return _node.Attributes.Cast().Select(WrapNode).ToList(); + } + } + + public IXmlNode ParentNode + { + get + { + XmlNode node = (_node is XmlAttribute) + ? ((XmlAttribute) _node).OwnerElement + : _node.ParentNode; + + if (node == null) + return null; + + return WrapNode(node); + } + } + + public string Value + { + get { return _node.Value; } + set { _node.Value = value; } + } + + public IXmlNode AppendChild(IXmlNode newChild) + { + XmlNodeWrapper xmlNodeWrapper = (XmlNodeWrapper) newChild; + _node.AppendChild(xmlNodeWrapper._node); + _childNodes = null; + + return newChild; + } + + public string NamespaceUri + { + get { return _node.NamespaceURI; } + } + } +#endif + #endregion + + #region Interfaces + internal interface IXmlDocument : IXmlNode + { + IXmlNode CreateComment(string text); + IXmlNode CreateTextNode(string text); + IXmlNode CreateCDataSection(string data); + IXmlNode CreateWhitespace(string text); + IXmlNode CreateSignificantWhitespace(string text); + IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone); + IXmlNode CreateXmlDocumentType(string name, string publicId, string systemId, string internalSubset); + IXmlNode CreateProcessingInstruction(string target, string data); + IXmlElement CreateElement(string elementName); + IXmlElement CreateElement(string qualifiedName, string namespaceUri); + IXmlNode CreateAttribute(string name, string value); + IXmlNode CreateAttribute(string qualifiedName, string namespaceUri, string value); + + IXmlElement DocumentElement { get; } + } + + internal interface IXmlDeclaration : IXmlNode + { + string Version { get; } + string Encoding { get; set; } + string Standalone { get; set; } + } + + internal interface IXmlDocumentType : IXmlNode + { + string Name { get; } + string System { get; } + string Public { get; } + string InternalSubset { get; } + } + + internal interface IXmlElement : IXmlNode + { + void SetAttributeNode(IXmlNode attribute); + string GetPrefixOfNamespace(string namespaceUri); + bool IsEmpty { get; } + } + + internal interface IXmlNode + { + XmlNodeType NodeType { get; } + string LocalName { get; } + IList ChildNodes { get; } + IList Attributes { get; } + IXmlNode ParentNode { get; } + string Value { get; set; } + IXmlNode AppendChild(IXmlNode newChild); + string NamespaceUri { get; } + object WrappedNode { get; } + } + #endregion + + #region XNodeWrappers +#if !NET20 + internal class XDeclarationWrapper : XObjectWrapper, IXmlDeclaration + { + internal XDeclaration Declaration { get; private set; } + + public XDeclarationWrapper(XDeclaration declaration) + : base(null) + { + Declaration = declaration; + } + + public override XmlNodeType NodeType + { + get { return XmlNodeType.XmlDeclaration; } + } + + public string Version + { + get { return Declaration.Version; } + } + + public string Encoding + { + get { return Declaration.Encoding; } + set { Declaration.Encoding = value; } + } + + public string Standalone + { + get { return Declaration.Standalone; } + set { Declaration.Standalone = value; } + } + } + + internal class XDocumentTypeWrapper : XObjectWrapper, IXmlDocumentType + { + private readonly XDocumentType _documentType; + + public XDocumentTypeWrapper(XDocumentType documentType) + : base(documentType) + { + _documentType = documentType; + } + + public string Name + { + get { return _documentType.Name; } + } + + public string System + { + get { return _documentType.SystemId; } + } + + public string Public + { + get { return _documentType.PublicId; } + } + + public string InternalSubset + { + get { return _documentType.InternalSubset; } + } + + public override string LocalName + { + get { return "DOCTYPE"; } + } + } + + internal class XDocumentWrapper : XContainerWrapper, IXmlDocument + { + private XDocument Document + { + get { return (XDocument)WrappedNode; } + } + + public XDocumentWrapper(XDocument document) + : base(document) + { + } + + public override IList ChildNodes + { + get + { + IList childNodes = base.ChildNodes; + + if (Document.Declaration != null && childNodes[0].NodeType != XmlNodeType.XmlDeclaration) + childNodes.Insert(0, new XDeclarationWrapper(Document.Declaration)); + + return childNodes; + } + } + + public IXmlNode CreateComment(string text) + { + return new XObjectWrapper(new XComment(text)); + } + + public IXmlNode CreateTextNode(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateCDataSection(string data) + { + return new XObjectWrapper(new XCData(data)); + } + + public IXmlNode CreateWhitespace(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateSignificantWhitespace(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone) + { + return new XDeclarationWrapper(new XDeclaration(version, encoding, standalone)); + } + + public IXmlNode CreateXmlDocumentType(string name, string publicId, string systemId, string internalSubset) + { + return new XDocumentTypeWrapper(new XDocumentType(name, publicId, systemId, internalSubset)); + } + + public IXmlNode CreateProcessingInstruction(string target, string data) + { + return new XProcessingInstructionWrapper(new XProcessingInstruction(target, data)); + } + + public IXmlElement CreateElement(string elementName) + { + return new XElementWrapper(new XElement(elementName)); + } + + public IXmlElement CreateElement(string qualifiedName, string namespaceUri) + { + string localName = MiscellaneousUtils.GetLocalName(qualifiedName); + return new XElementWrapper(new XElement(XName.Get(localName, namespaceUri))); + } + + public IXmlNode CreateAttribute(string name, string value) + { + return new XAttributeWrapper(new XAttribute(name, value)); + } + + public IXmlNode CreateAttribute(string qualifiedName, string namespaceUri, string value) + { + string localName = MiscellaneousUtils.GetLocalName(qualifiedName); + return new XAttributeWrapper(new XAttribute(XName.Get(localName, namespaceUri), value)); + } + + public IXmlElement DocumentElement + { + get + { + if (Document.Root == null) + return null; + + return new XElementWrapper(Document.Root); + } + } + + public override IXmlNode AppendChild(IXmlNode newChild) + { + XDeclarationWrapper declarationWrapper = newChild as XDeclarationWrapper; + if (declarationWrapper != null) + { + Document.Declaration = declarationWrapper.Declaration; + return declarationWrapper; + } + else + { + return base.AppendChild(newChild); + } + } + } + + internal class XTextWrapper : XObjectWrapper + { + private XText Text + { + get { return (XText)WrappedNode; } + } + + public XTextWrapper(XText text) + : base(text) + { + } + + public override string Value + { + get { return Text.Value; } + set { Text.Value = value; } + } + + public override IXmlNode ParentNode + { + get + { + if (Text.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Text.Parent); + } + } + } + + internal class XCommentWrapper : XObjectWrapper + { + private XComment Text + { + get { return (XComment)WrappedNode; } + } + + public XCommentWrapper(XComment text) + : base(text) + { + } + + public override string Value + { + get { return Text.Value; } + set { Text.Value = value; } + } + + public override IXmlNode ParentNode + { + get + { + if (Text.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Text.Parent); + } + } + } + + internal class XProcessingInstructionWrapper : XObjectWrapper + { + private XProcessingInstruction ProcessingInstruction + { + get { return (XProcessingInstruction)WrappedNode; } + } + + public XProcessingInstructionWrapper(XProcessingInstruction processingInstruction) + : base(processingInstruction) + { + } + + public override string LocalName + { + get { return ProcessingInstruction.Target; } + } + + public override string Value + { + get { return ProcessingInstruction.Data; } + set { ProcessingInstruction.Data = value; } + } + } + + internal class XContainerWrapper : XObjectWrapper + { + private IList _childNodes; + + private XContainer Container + { + get { return (XContainer)WrappedNode; } + } + + public XContainerWrapper(XContainer container) + : base(container) + { + } + + public override IList ChildNodes + { + get + { + // childnodes is read multiple times + // cache results to prevent multiple reads which kills perf in large documents + if (_childNodes == null) + _childNodes = Container.Nodes().Select(WrapNode).ToList(); + + return _childNodes; + } + } + + public override IXmlNode ParentNode + { + get + { + if (Container.Parent == null) + return null; + + return WrapNode(Container.Parent); + } + } + + internal static IXmlNode WrapNode(XObject node) + { + if (node is XDocument) + return new XDocumentWrapper((XDocument)node); + else if (node is XElement) + return new XElementWrapper((XElement)node); + else if (node is XContainer) + return new XContainerWrapper((XContainer)node); + else if (node is XProcessingInstruction) + return new XProcessingInstructionWrapper((XProcessingInstruction)node); + else if (node is XText) + return new XTextWrapper((XText)node); + else if (node is XComment) + return new XCommentWrapper((XComment)node); + else if (node is XAttribute) + return new XAttributeWrapper((XAttribute)node); + else if (node is XDocumentType) + return new XDocumentTypeWrapper((XDocumentType)node); + else + return new XObjectWrapper(node); + } + + public override IXmlNode AppendChild(IXmlNode newChild) + { + Container.Add(newChild.WrappedNode); + _childNodes = null; + + return newChild; + } + } + + internal class XObjectWrapper : IXmlNode + { + private readonly XObject _xmlObject; + + public XObjectWrapper(XObject xmlObject) + { + _xmlObject = xmlObject; + } + + public object WrappedNode + { + get { return _xmlObject; } + } + + public virtual XmlNodeType NodeType + { + get { return _xmlObject.NodeType; } + } + + public virtual string LocalName + { + get { return null; } + } + + public virtual IList ChildNodes + { + get { return new List(); } + } + + public virtual IList Attributes + { + get { return null; } + } + + public virtual IXmlNode ParentNode + { + get { return null; } + } + + public virtual string Value + { + get { return null; } + set { throw new InvalidOperationException(); } + } + + public virtual IXmlNode AppendChild(IXmlNode newChild) + { + throw new InvalidOperationException(); + } + + public virtual string NamespaceUri + { + get { return null; } + } + } + + internal class XAttributeWrapper : XObjectWrapper + { + private XAttribute Attribute + { + get { return (XAttribute)WrappedNode; } + } + + public XAttributeWrapper(XAttribute attribute) + : base(attribute) + { + } + + public override string Value + { + get { return Attribute.Value; } + set { Attribute.Value = value; } + } + + public override string LocalName + { + get { return Attribute.Name.LocalName; } + } + + public override string NamespaceUri + { + get { return Attribute.Name.NamespaceName; } + } + + public override IXmlNode ParentNode + { + get + { + if (Attribute.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Attribute.Parent); + } + } + } + + internal class XElementWrapper : XContainerWrapper, IXmlElement + { + private XElement Element + { + get { return (XElement)WrappedNode; } + } + + public XElementWrapper(XElement element) + : base(element) + { + } + + public void SetAttributeNode(IXmlNode attribute) + { + XObjectWrapper wrapper = (XObjectWrapper)attribute; + Element.Add(wrapper.WrappedNode); + } + + public override IList Attributes + { + get { return Element.Attributes().Select(a => new XAttributeWrapper(a)).Cast().ToList(); } + } + + public override string Value + { + get { return Element.Value; } + set { Element.Value = value; } + } + + public override string LocalName + { + get { return Element.Name.LocalName; } + } + + public override string NamespaceUri + { + get { return Element.Name.NamespaceName; } + } + + public string GetPrefixOfNamespace(string namespaceUri) + { + return Element.GetPrefixOfNamespace(namespaceUri); + } + + public bool IsEmpty + { + get { return Element.IsEmpty; } + } + } +#endif + #endregion + + /// + /// Converts XML to and from JSON. + /// + public class XmlNodeConverter : JsonConverter + { + private const string TextName = "#text"; + private const string CommentName = "#comment"; + private const string CDataName = "#cdata-section"; + private const string WhitespaceName = "#whitespace"; + private const string SignificantWhitespaceName = "#significant-whitespace"; + private const string DeclarationName = "?xml"; + private const string JsonNamespaceUri = "http://james.newtonking.com/projects/json"; + + /// + /// Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produces multiple root elements. + /// + /// The name of the deserialize root element. + public string DeserializeRootElementName { get; set; } + + /// + /// Gets or sets a flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// true if the array attibute is written to the XML; otherwise, false. + public bool WriteArrayAttribute { get; set; } + + /// + /// Gets or sets a value indicating whether to write the root JSON object. + /// + /// true if the JSON root object is omitted; otherwise, false. + public bool OmitRootObject { get; set; } + + #region Writing + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The calling serializer. + /// The value. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IXmlNode node = WrapXml(value); + + XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable()); + PushParentNamespaces(node, manager); + + if (!OmitRootObject) + writer.WriteStartObject(); + + SerializeNode(writer, node, manager, !OmitRootObject); + + if (!OmitRootObject) + writer.WriteEndObject(); + } + + private IXmlNode WrapXml(object value) + { +#if !NET20 + if (value is XObject) + return XContainerWrapper.WrapNode((XObject)value); +#endif +#if !(NETFX_CORE || PORTABLE) + if (value is XmlNode) + return XmlNodeWrapper.WrapNode((XmlNode)value); +#endif + + throw new ArgumentException("Value must be an XML object.", "value"); + } + + private void PushParentNamespaces(IXmlNode node, XmlNamespaceManager manager) + { + List parentElements = null; + + IXmlNode parent = node; + while ((parent = parent.ParentNode) != null) + { + if (parent.NodeType == XmlNodeType.Element) + { + if (parentElements == null) + parentElements = new List(); + + parentElements.Add(parent); + } + } + + if (parentElements != null) + { + parentElements.Reverse(); + + foreach (IXmlNode parentElement in parentElements) + { + manager.PushScope(); + foreach (IXmlNode attribute in parentElement.Attributes) + { + if (attribute.NamespaceUri == "http://www.w3.org/2000/xmlns/" && attribute.LocalName != "xmlns") + manager.AddNamespace(attribute.LocalName, attribute.Value); + } + } + } + } + + private string ResolveFullName(IXmlNode node, XmlNamespaceManager manager) + { + string prefix = (node.NamespaceUri == null || (node.LocalName == "xmlns" && node.NamespaceUri == "http://www.w3.org/2000/xmlns/")) + ? null + : manager.LookupPrefix(node.NamespaceUri); + + if (!string.IsNullOrEmpty(prefix)) + return prefix + ":" + node.LocalName; + else + return node.LocalName; + } + + private string GetPropertyName(IXmlNode node, XmlNamespaceManager manager) + { + switch (node.NodeType) + { + case XmlNodeType.Attribute: + if (node.NamespaceUri == JsonNamespaceUri) + return "$" + node.LocalName; + else + return "@" + ResolveFullName(node, manager); + case XmlNodeType.CDATA: + return CDataName; + case XmlNodeType.Comment: + return CommentName; + case XmlNodeType.Element: + return ResolveFullName(node, manager); + case XmlNodeType.ProcessingInstruction: + return "?" + ResolveFullName(node, manager); + case XmlNodeType.DocumentType: + return "!" + ResolveFullName(node, manager); + case XmlNodeType.XmlDeclaration: + return DeclarationName; + case XmlNodeType.SignificantWhitespace: + return SignificantWhitespaceName; + case XmlNodeType.Text: + return TextName; + case XmlNodeType.Whitespace: + return WhitespaceName; + default: + throw new JsonSerializationException("Unexpected XmlNodeType when getting node name: " + node.NodeType); + } + } + + private bool IsArray(IXmlNode node) + { + IXmlNode jsonArrayAttribute = (node.Attributes != null) + ? node.Attributes.SingleOrDefault(a => a.LocalName == "Array" && a.NamespaceUri == JsonNamespaceUri) + : null; + + return (jsonArrayAttribute != null && XmlConvert.ToBoolean(jsonArrayAttribute.Value)); + } + + private void SerializeGroupedNodes(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName) + { + // group nodes together by name + Dictionary> nodesGroupedByName = new Dictionary>(); + + for (int i = 0; i < node.ChildNodes.Count; i++) + { + IXmlNode childNode = node.ChildNodes[i]; + string nodeName = GetPropertyName(childNode, manager); + + List nodes; + if (!nodesGroupedByName.TryGetValue(nodeName, out nodes)) + { + nodes = new List(); + nodesGroupedByName.Add(nodeName, nodes); + } + + nodes.Add(childNode); + } + + // loop through grouped nodes. write single name instances as normal, + // write multiple names together in an array + foreach (KeyValuePair> nodeNameGroup in nodesGroupedByName) + { + List groupedNodes = nodeNameGroup.Value; + bool writeArray; + + if (groupedNodes.Count == 1) + { + writeArray = IsArray(groupedNodes[0]); + } + else + { + writeArray = true; + } + + if (!writeArray) + { + SerializeNode(writer, groupedNodes[0], manager, writePropertyName); + } + else + { + string elementNames = nodeNameGroup.Key; + + if (writePropertyName) + writer.WritePropertyName(elementNames); + + writer.WriteStartArray(); + + for (int i = 0; i < groupedNodes.Count; i++) + { + SerializeNode(writer, groupedNodes[i], manager, false); + } + + writer.WriteEndArray(); + } + } + } + + private void SerializeNode(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName) + { + switch (node.NodeType) + { + case XmlNodeType.Document: + case XmlNodeType.DocumentFragment: + SerializeGroupedNodes(writer, node, manager, writePropertyName); + break; + case XmlNodeType.Element: + if (IsArray(node) && node.ChildNodes.All(n => n.LocalName == node.LocalName) && node.ChildNodes.Count > 0) + { + SerializeGroupedNodes(writer, node, manager, false); + } + else + { + manager.PushScope(); + + foreach (IXmlNode attribute in node.Attributes) + { + if (attribute.NamespaceUri == "http://www.w3.org/2000/xmlns/") + { + string namespacePrefix = (attribute.LocalName != "xmlns") + ? attribute.LocalName + : string.Empty; + string namespaceUri = attribute.Value; + + manager.AddNamespace(namespacePrefix, namespaceUri); + } + } + + if (writePropertyName) + writer.WritePropertyName(GetPropertyName(node, manager)); + + if (!ValueAttributes(node.Attributes).Any() && node.ChildNodes.Count == 1 + && node.ChildNodes[0].NodeType == XmlNodeType.Text) + { + // write elements with a single text child as a name value pair + writer.WriteValue(node.ChildNodes[0].Value); + } + else if (node.ChildNodes.Count == 0 && CollectionUtils.IsNullOrEmpty(node.Attributes)) + { + IXmlElement element = (IXmlElement)node; + + // empty element + if (element.IsEmpty) + writer.WriteNull(); + else + writer.WriteValue(string.Empty); + } + else + { + writer.WriteStartObject(); + + for (int i = 0; i < node.Attributes.Count; i++) + { + SerializeNode(writer, node.Attributes[i], manager, true); + } + + SerializeGroupedNodes(writer, node, manager, true); + + writer.WriteEndObject(); + } + + manager.PopScope(); + } + + break; + case XmlNodeType.Comment: + if (writePropertyName) + writer.WriteComment(node.Value); + break; + case XmlNodeType.Attribute: + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.ProcessingInstruction: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + if (node.NamespaceUri == "http://www.w3.org/2000/xmlns/" && node.Value == JsonNamespaceUri) + return; + + if (node.NamespaceUri == JsonNamespaceUri) + { + if (node.LocalName == "Array") + return; + } + + if (writePropertyName) + writer.WritePropertyName(GetPropertyName(node, manager)); + writer.WriteValue(node.Value); + break; + case XmlNodeType.XmlDeclaration: + IXmlDeclaration declaration = (IXmlDeclaration)node; + writer.WritePropertyName(GetPropertyName(node, manager)); + writer.WriteStartObject(); + + if (!string.IsNullOrEmpty(declaration.Version)) + { + writer.WritePropertyName("@version"); + writer.WriteValue(declaration.Version); + } + if (!string.IsNullOrEmpty(declaration.Encoding)) + { + writer.WritePropertyName("@encoding"); + writer.WriteValue(declaration.Encoding); + } + if (!string.IsNullOrEmpty(declaration.Standalone)) + { + writer.WritePropertyName("@standalone"); + writer.WriteValue(declaration.Standalone); + } + + writer.WriteEndObject(); + break; + case XmlNodeType.DocumentType: + IXmlDocumentType documentType = (IXmlDocumentType)node; + writer.WritePropertyName(GetPropertyName(node, manager)); + writer.WriteStartObject(); + + if (!string.IsNullOrEmpty(documentType.Name)) + { + writer.WritePropertyName("@name"); + writer.WriteValue(documentType.Name); + } + if (!string.IsNullOrEmpty(documentType.Public)) + { + writer.WritePropertyName("@public"); + writer.WriteValue(documentType.Public); + } + if (!string.IsNullOrEmpty(documentType.System)) + { + writer.WritePropertyName("@system"); + writer.WriteValue(documentType.System); + } + if (!string.IsNullOrEmpty(documentType.InternalSubset)) + { + writer.WritePropertyName("@internalSubset"); + writer.WriteValue(documentType.InternalSubset); + } + + writer.WriteEndObject(); + break; + default: + throw new JsonSerializationException("Unexpected XmlNodeType when serializing nodes: " + node.NodeType); + } + } + #endregion + + #region Reading + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable()); + IXmlDocument document = null; + IXmlNode rootNode = null; + +#if !NET20 + if (typeof(XObject).IsAssignableFrom(objectType)) + { + if (objectType != typeof(XDocument) && objectType != typeof(XElement)) + throw new JsonSerializationException("XmlNodeConverter only supports deserializing XDocument or XElement."); + + XDocument d = new XDocument(); + document = new XDocumentWrapper(d); + rootNode = document; + } +#endif +#if !(NETFX_CORE || PORTABLE) + if (typeof(XmlNode).IsAssignableFrom(objectType)) + { + if (objectType != typeof(XmlDocument)) + throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments"); + + XmlDocument d = new XmlDocument(); + // prevent http request when resolving any DTD references + d.XmlResolver = null; + + document = new XmlDocumentWrapper(d); + rootNode = document; + } +#endif + + if (document == null || rootNode == null) + throw new JsonSerializationException("Unexpected type when converting XML: " + objectType); + + if (reader.TokenType != JsonToken.StartObject) + throw new JsonSerializationException("XmlNodeConverter can only convert JSON that begins with an object."); + + if (!string.IsNullOrEmpty(DeserializeRootElementName)) + { + //rootNode = document.CreateElement(DeserializeRootElementName); + //document.AppendChild(rootNode); + ReadElement(reader, document, rootNode, DeserializeRootElementName, manager); + } + else + { + reader.Read(); + DeserializeNode(reader, document, manager, rootNode); + } + +#if !NET20 + if (objectType == typeof(XElement)) + { + XElement element = (XElement)document.DocumentElement.WrappedNode; + element.Remove(); + + return element; + } +#endif + + return document.WrappedNode; + } + + private void DeserializeValue(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, string propertyName, IXmlNode currentNode) + { + switch (propertyName) + { + case TextName: + currentNode.AppendChild(document.CreateTextNode(reader.Value.ToString())); + break; + case CDataName: + currentNode.AppendChild(document.CreateCDataSection(reader.Value.ToString())); + break; + case WhitespaceName: + currentNode.AppendChild(document.CreateWhitespace(reader.Value.ToString())); + break; + case SignificantWhitespaceName: + currentNode.AppendChild(document.CreateSignificantWhitespace(reader.Value.ToString())); + break; + default: + // processing instructions and the xml declaration start with ? + if (!string.IsNullOrEmpty(propertyName) && propertyName[0] == '?') + { + CreateInstruction(reader, document, currentNode, propertyName); + } + else if (string.Equals(propertyName, "!DOCTYPE", StringComparison.OrdinalIgnoreCase)) + { + CreateDocumentType(reader, document, currentNode); + } + else + { + if (reader.TokenType == JsonToken.StartArray) + { + // handle nested arrays + ReadArrayElements(reader, document, propertyName, currentNode, manager); + return; + } + + // have to wait until attributes have been parsed before creating element + // attributes may contain namespace info used by the element + ReadElement(reader, document, currentNode, propertyName, manager); + } + break; + } + } + + private void ReadElement(JsonReader reader, IXmlDocument document, IXmlNode currentNode, string propertyName, XmlNamespaceManager manager) + { + if (string.IsNullOrEmpty(propertyName)) + throw new JsonSerializationException("XmlNodeConverter cannot convert JSON with an empty property name to XML."); + + Dictionary attributeNameValues = ReadAttributeElements(reader, manager); + + string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName); + + if (propertyName.StartsWith('@')) + { + string attributeName = propertyName.Substring(1); + string attributeValue = reader.Value.ToString(); + + string attributePrefix = MiscellaneousUtils.GetPrefix(attributeName); + + IXmlNode attribute = (!string.IsNullOrEmpty(attributePrefix)) + ? document.CreateAttribute(attributeName, manager.LookupNamespace(attributePrefix), attributeValue) + : document.CreateAttribute(attributeName, attributeValue); + + ((IXmlElement)currentNode).SetAttributeNode(attribute); + } + else + { + IXmlElement element = CreateElement(propertyName, document, elementPrefix, manager); + + currentNode.AppendChild(element); + + // add attributes to newly created element + foreach (KeyValuePair nameValue in attributeNameValues) + { + string attributePrefix = MiscellaneousUtils.GetPrefix(nameValue.Key); + + IXmlNode attribute = (!string.IsNullOrEmpty(attributePrefix)) + ? document.CreateAttribute(nameValue.Key, manager.LookupNamespace(attributePrefix), nameValue.Value) + : document.CreateAttribute(nameValue.Key, nameValue.Value); + + element.SetAttributeNode(attribute); + } + + if (reader.TokenType == JsonToken.String + || reader.TokenType == JsonToken.Integer + || reader.TokenType == JsonToken.Float + || reader.TokenType == JsonToken.Boolean + || reader.TokenType == JsonToken.Date) + { + element.AppendChild(document.CreateTextNode(ConvertTokenToXmlValue(reader))); + } + else if (reader.TokenType == JsonToken.Null) + { + // empty element. do nothing + } + else + { + // finished element will have no children to deserialize + if (reader.TokenType != JsonToken.EndObject) + { + manager.PushScope(); + DeserializeNode(reader, document, manager, element); + manager.PopScope(); + } + + manager.RemoveNamespace(string.Empty, manager.DefaultNamespace); + } + } + } + + private string ConvertTokenToXmlValue(JsonReader reader) + { + if (reader.TokenType == JsonToken.String) + { + return reader.Value.ToString(); + } + else if (reader.TokenType == JsonToken.Integer) + { + return XmlConvert.ToString(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Float) + { + if (reader.Value is decimal) + return XmlConvert.ToString((decimal)reader.Value); + if (reader.Value is float) + return XmlConvert.ToString((float)reader.Value); + + return XmlConvert.ToString(Convert.ToDouble(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Boolean) + { + return XmlConvert.ToString(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Date) + { +#if !NET20 + if (reader.Value is DateTimeOffset) + return XmlConvert.ToString((DateTimeOffset)reader.Value); +#endif + + DateTime d = Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture); +#if !(NETFX_CORE || PORTABLE) + return XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind)); +#else + return XmlConvert.ToString(d); +#endif + } + else if (reader.TokenType == JsonToken.Null) + { + return null; + } + else + { + throw JsonSerializationException.Create(reader, "Cannot get an XML string value from token type '{0}'.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private void ReadArrayElements(JsonReader reader, IXmlDocument document, string propertyName, IXmlNode currentNode, XmlNamespaceManager manager) + { + string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName); + + IXmlElement nestedArrayElement = CreateElement(propertyName, document, elementPrefix, manager); + + currentNode.AppendChild(nestedArrayElement); + + int count = 0; + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + DeserializeValue(reader, document, manager, propertyName, nestedArrayElement); + count++; + } + + if (WriteArrayAttribute) + { + AddJsonArrayAttribute(nestedArrayElement, document); + } + + if (count == 1 && WriteArrayAttribute) + { + IXmlElement arrayElement = nestedArrayElement.ChildNodes.OfType().Single(n => n.LocalName == propertyName); + AddJsonArrayAttribute(arrayElement, document); + } + } + + private void AddJsonArrayAttribute(IXmlElement element, IXmlDocument document) + { + element.SetAttributeNode(document.CreateAttribute("json:Array", JsonNamespaceUri, "true")); + +#if !NET20 + // linq to xml doesn't automatically include prefixes via the namespace manager + if (element is XElementWrapper) + { + if (element.GetPrefixOfNamespace(JsonNamespaceUri) == null) + { + element.SetAttributeNode(document.CreateAttribute("xmlns:json", "http://www.w3.org/2000/xmlns/", JsonNamespaceUri)); + } + } +#endif + } + + private Dictionary ReadAttributeElements(JsonReader reader, XmlNamespaceManager manager) + { + Dictionary attributeNameValues = new Dictionary(); + bool finishedAttributes = false; + bool finishedElement = false; + + // a string token means the element only has a single text child + if (reader.TokenType != JsonToken.String + && reader.TokenType != JsonToken.Null + && reader.TokenType != JsonToken.Boolean + && reader.TokenType != JsonToken.Integer + && reader.TokenType != JsonToken.Float + && reader.TokenType != JsonToken.Date + && reader.TokenType != JsonToken.StartConstructor) + { + // read properties until first non-attribute is encountered + while (!finishedAttributes && !finishedElement && reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string attributeName = reader.Value.ToString(); + + if (!string.IsNullOrEmpty(attributeName)) + { + char firstChar = attributeName[0]; + string attributeValue; + + switch (firstChar) + { + case '@': + attributeName = attributeName.Substring(1); + reader.Read(); + attributeValue = ConvertTokenToXmlValue(reader); + attributeNameValues.Add(attributeName, attributeValue); + + string namespacePrefix; + if (IsNamespaceAttribute(attributeName, out namespacePrefix)) + { + manager.AddNamespace(namespacePrefix, attributeValue); + } + break; + case '$': + attributeName = attributeName.Substring(1); + reader.Read(); + attributeValue = reader.Value.ToString(); + + // check that JsonNamespaceUri is in scope + // if it isn't then add it to document and namespace manager + string jsonPrefix = manager.LookupPrefix(JsonNamespaceUri); + if (jsonPrefix == null) + { + // ensure that the prefix used is free + int? i = null; + while (manager.LookupNamespace("json" + i) != null) + { + i = i.GetValueOrDefault() + 1; + } + jsonPrefix = "json" + i; + + attributeNameValues.Add("xmlns:" + jsonPrefix, JsonNamespaceUri); + manager.AddNamespace(jsonPrefix, JsonNamespaceUri); + } + + attributeNameValues.Add(jsonPrefix + ":" + attributeName, attributeValue); + break; + default: + finishedAttributes = true; + break; + } + } + else + { + + finishedAttributes = true; + } + + break; + case JsonToken.EndObject: + finishedElement = true; + break; + case JsonToken.Comment: + finishedElement = true; + break; + default: + throw new JsonSerializationException("Unexpected JsonToken: " + reader.TokenType); + } + } + } + + return attributeNameValues; + } + + private void CreateInstruction(JsonReader reader, IXmlDocument document, IXmlNode currentNode, string propertyName) + { + if (propertyName == DeclarationName) + { + string version = null; + string encoding = null; + string standalone = null; + while (reader.Read() && reader.TokenType != JsonToken.EndObject) + { + switch (reader.Value.ToString()) + { + case "@version": + reader.Read(); + version = reader.Value.ToString(); + break; + case "@encoding": + reader.Read(); + encoding = reader.Value.ToString(); + break; + case "@standalone": + reader.Read(); + standalone = reader.Value.ToString(); + break; + default: + throw new JsonSerializationException("Unexpected property name encountered while deserializing XmlDeclaration: " + reader.Value); + } + } + + IXmlNode declaration = document.CreateXmlDeclaration(version, encoding, standalone); + currentNode.AppendChild(declaration); + } + else + { + IXmlNode instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString()); + currentNode.AppendChild(instruction); + } + } + + private void CreateDocumentType(JsonReader reader, IXmlDocument document, IXmlNode currentNode) + { + string name = null; + string publicId = null; + string systemId = null; + string internalSubset = null; + while (reader.Read() && reader.TokenType != JsonToken.EndObject) + { + switch (reader.Value.ToString()) + { + case "@name": + reader.Read(); + name = reader.Value.ToString(); + break; + case "@public": + reader.Read(); + publicId = reader.Value.ToString(); + break; + case "@system": + reader.Read(); + systemId = reader.Value.ToString(); + break; + case "@internalSubset": + reader.Read(); + internalSubset = reader.Value.ToString(); + break; + default: + throw new JsonSerializationException("Unexpected property name encountered while deserializing XmlDeclaration: " + reader.Value); + } + } + + IXmlNode documentType = document.CreateXmlDocumentType(name, publicId, systemId, internalSubset); + currentNode.AppendChild(documentType); + } + + private IXmlElement CreateElement(string elementName, IXmlDocument document, string elementPrefix, XmlNamespaceManager manager) + { + string ns = string.IsNullOrEmpty(elementPrefix) ? manager.DefaultNamespace : manager.LookupNamespace(elementPrefix); + + IXmlElement element = (!string.IsNullOrEmpty(ns)) ? document.CreateElement(elementName, ns) : document.CreateElement(elementName); + + return element; + } + + private void DeserializeNode(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, IXmlNode currentNode) + { + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + if (currentNode.NodeType == XmlNodeType.Document && document.DocumentElement != null) + throw new JsonSerializationException("JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName."); + + string propertyName = reader.Value.ToString(); + reader.Read(); + + if (reader.TokenType == JsonToken.StartArray) + { + int count = 0; + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + DeserializeValue(reader, document, manager, propertyName, currentNode); + count++; + } + + if (count == 1 && WriteArrayAttribute) + { + IXmlElement arrayElement = currentNode.ChildNodes.OfType().Single(n => n.LocalName == propertyName); + AddJsonArrayAttribute(arrayElement, document); + } + } + else + { + DeserializeValue(reader, document, manager, propertyName, currentNode); + } + break; + case JsonToken.StartConstructor: + string constructorName = reader.Value.ToString(); + + while (reader.Read() && reader.TokenType != JsonToken.EndConstructor) + { + DeserializeValue(reader, document, manager, constructorName, currentNode); + } + break; + case JsonToken.Comment: + currentNode.AppendChild(document.CreateComment((string)reader.Value)); + break; + case JsonToken.EndObject: + case JsonToken.EndArray: + return; + default: + throw new JsonSerializationException("Unexpected JsonToken when deserializing node: " + reader.TokenType); + } + } while (reader.TokenType == JsonToken.PropertyName || reader.Read()); + // don't read if current token is a property. token was already read when parsing element attributes + } + + /// + /// Checks if the attributeName is a namespace attribute. + /// + /// Attribute name to test. + /// The attribute name prefix if it has one, otherwise an empty string. + /// True if attribute name is for a namespace attribute, otherwise false. + private bool IsNamespaceAttribute(string attributeName, out string prefix) + { + if (attributeName.StartsWith("xmlns", StringComparison.Ordinal)) + { + if (attributeName.Length == 5) + { + prefix = string.Empty; + return true; + } + else if (attributeName[5] == ':') + { + prefix = attributeName.Substring(6, attributeName.Length - 6); + return true; + } + } + prefix = null; + return false; + } + + private IEnumerable ValueAttributes(IEnumerable c) + { + return c.Where(a => a.NamespaceUri != JsonNamespaceUri); + } + #endregion + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { +#if !NET20 + if (typeof(XObject).IsAssignableFrom(valueType)) + return true; +#endif +#if !(NETFX_CORE || PORTABLE) + if (typeof(XmlNode).IsAssignableFrom(valueType)) + return true; +#endif + + return false; + } + } +} + +#endif diff --git a/Libs/JsonNet/DateFormatHandling.cs b/Libs/JsonNet/DateFormatHandling.cs new file mode 100644 index 0000000..e26feec --- /dev/null +++ b/Libs/JsonNet/DateFormatHandling.cs @@ -0,0 +1,43 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how dates are formatted when writing JSON text. + /// + public enum DateFormatHandling + { + /// + /// Dates are written in the ISO 8601 format, e.g. "2012-03-21T05:40Z". + /// + IsoDateFormat, + + /// + /// Dates are written in the Microsoft JSON format, e.g. "\/Date(1198908717056)\/". + /// + MicrosoftDateFormat + } +} \ No newline at end of file diff --git a/Libs/JsonNet/DateParseHandling.cs b/Libs/JsonNet/DateParseHandling.cs new file mode 100644 index 0000000..84abaf5 --- /dev/null +++ b/Libs/JsonNet/DateParseHandling.cs @@ -0,0 +1,49 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON text. + /// + public enum DateParseHandling + { + /// + /// Date formatted strings are not parsed to a date type and are read as strings. + /// + None, + + /// + /// Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . + /// + DateTime, +#if !NET20 + /// + /// Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . + /// + DateTimeOffset +#endif + } +} \ No newline at end of file diff --git a/Libs/JsonNet/DateTimeZoneHandling.cs b/Libs/JsonNet/DateTimeZoneHandling.cs new file mode 100644 index 0000000..df88908 --- /dev/null +++ b/Libs/JsonNet/DateTimeZoneHandling.cs @@ -0,0 +1,56 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Specifies how to treat the time value when converting between string and . + /// + public enum DateTimeZoneHandling + { + /// + /// Treat as local time. If the object represents a Coordinated Universal Time (UTC), it is converted to the local time. + /// + Local, + + /// + /// Treat as a UTC. If the object represents a local time, it is converted to a UTC. + /// + Utc, + + /// + /// Treat as a local time if a is being converted to a string. + /// If a string is being converted to , convert to a local time if a time zone is specified. + /// + Unspecified, + + /// + /// Time zone information should be preserved when converting. + /// + RoundtripKind + } +} \ No newline at end of file diff --git a/Libs/JsonNet/DefaultValueHandling.cs b/Libs/JsonNet/DefaultValueHandling.cs new file mode 100644 index 0000000..6125e92 --- /dev/null +++ b/Libs/JsonNet/DefaultValueHandling.cs @@ -0,0 +1,67 @@ +#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.ComponentModel; + +namespace Newtonsoft.Json +{ + /// + /// Specifies default value handling options for the . + /// + /// + /// + /// + /// + [Flags] + public enum DefaultValueHandling + { + /// + /// Include members where the member value is the same as the member's default value when serializing objects. + /// Included members are written to JSON. Has no effect when deserializing. + /// + Include = 0, + + /// + /// Ignore members where the member value is the same as the member's default value when serializing objects + /// so that is is not written to JSON. + /// This option will ignore all default values (e.g. null for objects and nullable types; 0 for integers, + /// decimals and floating point numbers; and false for booleans). The default value ignored can be changed by + /// placing the on the property. + /// + Ignore = 1, + + /// + /// Members with a default value but no JSON will be set to their default value when deserializing. + /// + Populate = 2, + + /// + /// Ignore members where the member value is the same as the member's default value when serializing objects + /// and sets members to their default value when deserializing. + /// + IgnoreAndPopulate = Ignore | Populate + } +} diff --git a/Libs/JsonNet/FloatFormatHandling.cs b/Libs/JsonNet/FloatFormatHandling.cs new file mode 100644 index 0000000..f39f7cc --- /dev/null +++ b/Libs/JsonNet/FloatFormatHandling.cs @@ -0,0 +1,50 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies float format handling options when writing special floating point numbers, e.g. , + /// and with . + /// + public enum FloatFormatHandling + { + /// + /// Write special floating point values as strings in JSON, e.g. "NaN", "Infinity", "-Infinity". + /// + String, + + /// + /// Write special floating point values as symbols in JSON, e.g. NaN, Infinity, -Infinity. + /// Note that this will produce non-valid JSON. + /// + Symbol, + + /// + /// Write special floating point values as the property's default value in JSON, e.g. 0.0 for a property, null for a property. + /// + DefaultValue + } +} \ No newline at end of file diff --git a/Libs/JsonNet/FloatParseHandling.cs b/Libs/JsonNet/FloatParseHandling.cs new file mode 100644 index 0000000..093d6e9 --- /dev/null +++ b/Libs/JsonNet/FloatParseHandling.cs @@ -0,0 +1,43 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + /// + public enum FloatParseHandling + { + /// + /// Floating point numbers are parsed to . + /// + Double, + + /// + /// Floating point numbers are parsed to . + /// + Decimal + } +} \ No newline at end of file diff --git a/Libs/JsonNet/FormatterAssemblyStyle.cs b/Libs/JsonNet/FormatterAssemblyStyle.cs new file mode 100644 index 0000000..0029781 --- /dev/null +++ b/Libs/JsonNet/FormatterAssemblyStyle.cs @@ -0,0 +1,23 @@ + +#if PocketPC || NETFX_CORE || PORTABLE40 || PORTABLE + +namespace System.Runtime.Serialization.Formatters +{ + /// + /// Indicates the method that will be used during deserialization for locating and loading assemblies. + /// + public enum FormatterAssemblyStyle + { + /// + /// In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method is used to load the assembly. + /// + Simple, + + /// + /// In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the Assembly class is used to load the assembly. + /// + Full + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Formatting.cs b/Libs/JsonNet/Formatting.cs new file mode 100644 index 0000000..55043b5 --- /dev/null +++ b/Libs/JsonNet/Formatting.cs @@ -0,0 +1,43 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies formatting options for the . + /// + public enum Formatting + { + /// + /// No special formatting is applied. This is the default. + /// + None, + + /// + /// Causes child objects to be indented according to the and settings. + /// + Indented + } +} \ No newline at end of file diff --git a/Libs/JsonNet/IJsonLineInfo.cs b/Libs/JsonNet/IJsonLineInfo.cs new file mode 100644 index 0000000..b71aedf --- /dev/null +++ b/Libs/JsonNet/IJsonLineInfo.cs @@ -0,0 +1,53 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Provides an interface to enable a class to return line and position information. + /// + public interface IJsonLineInfo + { + /// + /// Gets a value indicating whether the class can return line information. + /// + /// + /// true if LineNumber and LinePosition can be provided; otherwise, false. + /// + bool HasLineInfo(); + + /// + /// Gets the current line number. + /// + /// The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + int LineNumber { get; } + + /// + /// Gets the current line position. + /// + /// The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + int LinePosition { get; } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonArrayAttribute.cs b/Libs/JsonNet/JsonArrayAttribute.cs new file mode 100644 index 0000000..bbfc90d --- /dev/null +++ b/Libs/JsonNet/JsonArrayAttribute.cs @@ -0,0 +1,73 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the collection. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonArrayAttribute : JsonContainerAttribute + { + private bool _allowNullItems; + + /// + /// Gets or sets a value indicating whether null items are allowed in the collection. + /// + /// true if null items are allowed in the collection; otherwise, false. + public bool AllowNullItems + { + get { return _allowNullItems; } + set { _allowNullItems = value; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonArrayAttribute() + { + } + + /// + /// Initializes a new instance of the class with a flag indicating whether the array can contain null items + /// + /// A flag indicating whether the array can contain null items. + public JsonArrayAttribute(bool allowNullItems) + { + _allowNullItems = allowNullItems; + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + public JsonArrayAttribute(string id) + : base(id) + { + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonConstructorAttribute.cs b/Libs/JsonNet/JsonConstructorAttribute.cs new file mode 100644 index 0000000..cd5d1bb --- /dev/null +++ b/Libs/JsonNet/JsonConstructorAttribute.cs @@ -0,0 +1,37 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to use the specified constructor when deserializing that object. + /// + [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false)] + public sealed class JsonConstructorAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonContainerAttribute.cs b/Libs/JsonNet/JsonContainerAttribute.cs new file mode 100644 index 0000000..de77739 --- /dev/null +++ b/Libs/JsonNet/JsonContainerAttribute.cs @@ -0,0 +1,138 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the object. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public abstract class JsonContainerAttribute : Attribute + { + /// + /// Gets or sets the id. + /// + /// The id. + public string Id { get; set; } + + /// + /// Gets or sets the title. + /// + /// The title. + public string Title { get; set; } + + /// + /// Gets or sets the description. + /// + /// The description. + public string Description { get; set; } + + /// + /// Gets the collection's items converter. + /// + /// The collection's items converter. + public Type ItemConverterType { get; set; } + + /// + /// The parameter list to use when constructing the JsonConverter described by ItemConverterType. + /// If null, the default constructor is used. + /// When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number, + /// order, and type of these parameters. + /// + /// + /// [JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] + /// + public object[] ItemConverterParameters { get; set; } + + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal bool? _isReference; + internal bool? _itemIsReference; + internal ReferenceLoopHandling? _itemReferenceLoopHandling; + internal TypeNameHandling? _itemTypeNameHandling; + + /// + /// Gets or sets a value that indicates whether to preserve object references. + /// + /// + /// true to keep object reference; otherwise, false. The default is false. + /// + public bool IsReference + { + get { return _isReference ?? default(bool); } + set { _isReference = value; } + } + + /// + /// Gets or sets a value that indicates whether to preserve collection's items references. + /// + /// + /// true to keep collection's items object references; otherwise, false. The default is false. + /// + public bool ItemIsReference + { + get { return _itemIsReference ?? default(bool); } + set { _itemIsReference = value; } + } + + /// + /// Gets or sets the reference loop handling used when serializing the collection's items. + /// + /// The reference loop handling. + public ReferenceLoopHandling ItemReferenceLoopHandling + { + get { return _itemReferenceLoopHandling ?? default(ReferenceLoopHandling); } + set { _itemReferenceLoopHandling = value; } + } + + /// + /// Gets or sets the type name handling used when serializing the collection's items. + /// + /// The type name handling. + public TypeNameHandling ItemTypeNameHandling + { + get { return _itemTypeNameHandling ?? default(TypeNameHandling); } + set { _itemTypeNameHandling = value; } + } + + /// + /// Initializes a new instance of the class. + /// + protected JsonContainerAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + protected JsonContainerAttribute(string id) + { + Id = id; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonConvert.cs b/Libs/JsonNet/JsonConvert.cs new file mode 100644 index 0000000..3176dcf --- /dev/null +++ b/Libs/JsonNet/JsonConvert.cs @@ -0,0 +1,1130 @@ +#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.IO; +using System.Globalization; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +#if !(NET20 || NET35 || PORTABLE40) +using System.Threading.Tasks; +#endif +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using System.Xml; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using System.Text; +#if !(NET20 || PORTABLE40) +using System.Xml.Linq; +#endif + +namespace Newtonsoft.Json +{ + /// + /// Provides methods for converting between common language runtime types and JSON types. + /// + /// + /// + /// + public static class JsonConvert + { + /// + /// Gets or sets a function that creates default . + /// Default settings are automatically used by serialization methods on , + /// and and on . + /// To serialize without using any default settings create a with + /// . + /// + public static Func DefaultSettings { get; set; } + + /// + /// Represents JavaScript's boolean value true as a string. This field is read-only. + /// + public static readonly string True = "true"; + + /// + /// Represents JavaScript's boolean value false as a string. This field is read-only. + /// + public static readonly string False = "false"; + + /// + /// Represents JavaScript's null as a string. This field is read-only. + /// + public static readonly string Null = "null"; + + /// + /// Represents JavaScript's undefined as a string. This field is read-only. + /// + public static readonly string Undefined = "undefined"; + + /// + /// Represents JavaScript's positive infinity as a string. This field is read-only. + /// + public static readonly string PositiveInfinity = "Infinity"; + + /// + /// Represents JavaScript's negative infinity as a string. This field is read-only. + /// + public static readonly string NegativeInfinity = "-Infinity"; + + /// + /// Represents JavaScript's NaN as a string. This field is read-only. + /// + public static readonly string NaN = "NaN"; + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(DateTime value) + { + return ToString(value, DateFormatHandling.IsoDateFormat, DateTimeZoneHandling.RoundtripKind); + } + + /// + /// Converts the to its JSON string representation using the specified. + /// + /// The value to convert. + /// The format the date will be converted to. + /// The time zone handling when the date is converted to a string. + /// A JSON string representation of the . + public static string ToString(DateTime value, DateFormatHandling format, DateTimeZoneHandling timeZoneHandling) + { + DateTime updatedDateTime = DateTimeUtils.EnsureDateTime(value, timeZoneHandling); + + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + writer.Write('"'); + DateTimeUtils.WriteDateTimeString(writer, updatedDateTime, format, null, CultureInfo.InvariantCulture); + writer.Write('"'); + return writer.ToString(); + } + } + +#if !NET20 + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(DateTimeOffset value) + { + return ToString(value, DateFormatHandling.IsoDateFormat); + } + + /// + /// Converts the to its JSON string representation using the specified. + /// + /// The value to convert. + /// The format the date will be converted to. + /// A JSON string representation of the . + public static string ToString(DateTimeOffset value, DateFormatHandling format) + { + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + writer.Write('"'); + DateTimeUtils.WriteDateTimeOffsetString(writer, value, format, null, CultureInfo.InvariantCulture); + writer.Write('"'); + return writer.ToString(); + } + } +#endif + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(bool value) + { + return (value) ? True : False; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(char value) + { + return ToString(char.ToString(value)); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(Enum value) + { + return value.ToString("D"); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(int value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(short value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(ushort value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(uint value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(long value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + private static string ToStringInternal(BigInteger value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } +#endif + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(ulong value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(float value) + { + return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); + } + + internal static string ToString(float value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) + { + return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); + } + + private static string EnsureFloatFormat(double value, string text, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) + { + if (floatFormatHandling == FloatFormatHandling.Symbol || !(double.IsInfinity(value) || double.IsNaN(value))) + return text; + + if (floatFormatHandling == FloatFormatHandling.DefaultValue) + return (!nullable) ? "0.0" : Null; + + return quoteChar + text + quoteChar; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(double value) + { + return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); + } + + internal static string ToString(double value, FloatFormatHandling floatFormatHandling, char quoteChar, bool nullable) + { + return EnsureFloatFormat(value, EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)), floatFormatHandling, quoteChar, nullable); + } + + private static string EnsureDecimalPlace(double value, string text) + { + if (double.IsNaN(value) || double.IsInfinity(value) || text.IndexOf('.') != -1 || text.IndexOf('E') != -1 || text.IndexOf('e') != -1) + return text; + + return text + ".0"; + } + + private static string EnsureDecimalPlace(string text) + { + if (text.IndexOf('.') != -1) + return text; + + return text + ".0"; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(byte value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(sbyte value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(decimal value) + { + return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture)); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(Guid value) + { + return ToString(value, '"'); + } + + internal static string ToString(Guid value, char quoteChar) + { + string text; + string qc; +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + text = value.ToString("D", CultureInfo.InvariantCulture); + qc = quoteChar.ToString(CultureInfo.InvariantCulture); +#else + text = value.ToString("D"); + qc = quoteChar.ToString(); +#endif + + return qc + text + qc; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(TimeSpan value) + { + return ToString(value, '"'); + } + + internal static string ToString(TimeSpan value, char quoteChar) + { + return ToString(value.ToString(), quoteChar); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(Uri value) + { + if (value == null) + return Null; + + return ToString(value, '"'); + } + + internal static string ToString(Uri value, char quoteChar) + { + return ToString(value.OriginalString, quoteChar); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(string value) + { + return ToString(value, '"'); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// The string delimiter character. + /// A JSON string representation of the . + public static string ToString(string value, char delimiter) + { + return ToString(value, delimiter, StringEscapeHandling.Default); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// The string delimiter character. + /// The string escape handling. + /// A JSON string representation of the . + public static string ToString(string value, char delimiter, StringEscapeHandling stringEscapeHandling) + { + if (delimiter != '"' && delimiter != '\'') + throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter"); + + return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, true, stringEscapeHandling); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(object value) + { + if (value == null) + return Null; + + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(value.GetType()); + + switch (typeCode) + { + case PrimitiveTypeCode.String: + return ToString((string)value); + case PrimitiveTypeCode.Char: + return ToString((char)value); + case PrimitiveTypeCode.Boolean: + return ToString((bool)value); + case PrimitiveTypeCode.SByte: + return ToString((sbyte)value); + case PrimitiveTypeCode.Int16: + return ToString((short)value); + case PrimitiveTypeCode.UInt16: + return ToString((ushort)value); + case PrimitiveTypeCode.Int32: + return ToString((int)value); + case PrimitiveTypeCode.Byte: + return ToString((byte)value); + case PrimitiveTypeCode.UInt32: + return ToString((uint)value); + case PrimitiveTypeCode.Int64: + return ToString((long)value); + case PrimitiveTypeCode.UInt64: + return ToString((ulong)value); + case PrimitiveTypeCode.Single: + return ToString((float)value); + case PrimitiveTypeCode.Double: + return ToString((double)value); + case PrimitiveTypeCode.DateTime: + return ToString((DateTime)value); + case PrimitiveTypeCode.Decimal: + return ToString((decimal)value); +#if !(NETFX_CORE || PORTABLE) + case PrimitiveTypeCode.DBNull: + return Null; +#endif +#if !NET20 + case PrimitiveTypeCode.DateTimeOffset: + return ToString((DateTimeOffset)value); +#endif + case PrimitiveTypeCode.Guid: + return ToString((Guid)value); + case PrimitiveTypeCode.Uri: + return ToString((Uri)value); + case PrimitiveTypeCode.TimeSpan: + return ToString((TimeSpan)value); +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + case PrimitiveTypeCode.BigInteger: + return ToStringInternal((BigInteger)value); +#endif + } + + throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + #region Serialize + /// + /// Serializes the specified object to a JSON string. + /// + /// The object to serialize. + /// A JSON string representation of the object. + public static string SerializeObject(object value) + { + return SerializeObject(value, null, (JsonSerializerSettings)null); + } + + /// + /// Serializes the specified object to a JSON string using formatting. + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Formatting formatting) + { + return SerializeObject(value, formatting, (JsonSerializerSettings)null); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// A collection converters used while serializing. + /// A JSON string representation of the object. + public static string SerializeObject(object value, params JsonConverter[] converters) + { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return SerializeObject(value, null, settings); + } + + /// + /// Serializes the specified object to a JSON string using formatting and a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// A collection converters used while serializing. + /// A JSON string representation of the object. + public static string SerializeObject(object value, Formatting formatting, params JsonConverter[] converters) + { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return SerializeObject(value, null, formatting, settings); + } + + /// + /// Serializes the specified object to a JSON string using . + /// + /// The object to serialize. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, JsonSerializerSettings settings) + { + return SerializeObject(value, null, settings); + } + + /// + /// Serializes the specified object to a JSON string using a type, formatting and . + /// + /// The object to serialize. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The type of the value being serialized. + /// This parameter is used when is Auto to write out the type name if the type of the value does not match. + /// Specifing the type is optional. + /// + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Type type, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + + return SerializeObjectInternal(value, type, jsonSerializer); + } + + /// + /// Serializes the specified object to a JSON string using formatting and . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Formatting formatting, JsonSerializerSettings settings) + { + return SerializeObject(value, null, formatting, settings); + } + + /// + /// Serializes the specified object to a JSON string using a type, formatting and . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The type of the value being serialized. + /// This parameter is used when is Auto to write out the type name if the type of the value does not match. + /// Specifing the type is optional. + /// + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Type type, Formatting formatting, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + jsonSerializer.Formatting = formatting; + + return SerializeObjectInternal(value, type, jsonSerializer); + } + + private static string SerializeObjectInternal(object value, Type type, JsonSerializer jsonSerializer) + { + StringBuilder sb = new StringBuilder(256); + StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = jsonSerializer.Formatting; + + jsonSerializer.Serialize(jsonWriter, value, type); + } + + return sw.ToString(); + } + +#if !(NET20 || NET35 || PORTABLE40) + /// + /// Asynchronously serializes the specified object to a JSON string. + /// Serialization will happen on a new thread. + /// + /// The object to serialize. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.SerializeObject(value))")] + public static Task SerializeObjectAsync(object value) + { + return SerializeObjectAsync(value, Formatting.None, null); + } + + /// + /// Asynchronously serializes the specified object to a JSON string using formatting. + /// Serialization will happen on a new thread. + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.SerializeObject(value, formatting))")] + public static Task SerializeObjectAsync(object value, Formatting formatting) + { + return SerializeObjectAsync(value, formatting, null); + } + + /// + /// Asynchronously serializes the specified object to a JSON string using formatting and a collection of . + /// Serialization will happen on a new thread. + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.SerializeObject(value, formatting, settings))")] + public static Task SerializeObjectAsync(object value, Formatting formatting, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => SerializeObject(value, formatting, settings)); + } +#endif + #endregion + + #region Deserialize + /// + /// Deserializes the JSON to a .NET object. + /// + /// The JSON to deserialize. + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value) + { + return DeserializeObject(value, null, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to a .NET object using . + /// + /// The JSON to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, JsonSerializerSettings settings) + { + return DeserializeObject(value, null, settings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The of object being deserialized. + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type) + { + return DeserializeObject(value, type, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// The deserialized object from the JSON string. + public static T DeserializeObject(string value) + { + return DeserializeObject(value, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to the given anonymous type. + /// + /// + /// The anonymous type to deserialize to. This can't be specified + /// traditionally and must be infered from the anonymous type passed + /// as a parameter. + /// + /// The JSON to deserialize. + /// The anonymous type object. + /// The deserialized anonymous type from the JSON string. + public static T DeserializeAnonymousType(string value, T anonymousTypeObject) + { + return DeserializeObject(value); + } + + /// + /// Deserializes the JSON to the given anonymous type using . + /// + /// + /// The anonymous type to deserialize to. This can't be specified + /// traditionally and must be infered from the anonymous type passed + /// as a parameter. + /// + /// The JSON to deserialize. + /// The anonymous type object. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The deserialized anonymous type from the JSON string. + public static T DeserializeAnonymousType(string value, T anonymousTypeObject, JsonSerializerSettings settings) + { + return DeserializeObject(value, settings); + } + + /// + /// Deserializes the JSON to the specified .NET type using a collection of . + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// Converters to use while deserializing. + /// The deserialized object from the JSON string. + public static T DeserializeObject(string value, params JsonConverter[] converters) + { + return (T)DeserializeObject(value, typeof(T), converters); + } + + /// + /// Deserializes the JSON to the specified .NET type using . + /// + /// The type of the object to deserialize to. + /// The object to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The deserialized object from the JSON string. + public static T DeserializeObject(string value, JsonSerializerSettings settings) + { + return (T)DeserializeObject(value, typeof(T), settings); + } + + /// + /// Deserializes the JSON to the specified .NET type using a collection of . + /// + /// The JSON to deserialize. + /// The type of the object to deserialize. + /// Converters to use while deserializing. + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type, params JsonConverter[] converters) + { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return DeserializeObject(value, type, settings); + } + + /// + /// Deserializes the JSON to the specified .NET type using . + /// + /// The JSON to deserialize. + /// The type of the object to deserialize to. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type, JsonSerializerSettings settings) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + + // by default DeserializeObject should check for additional content + if (!jsonSerializer.IsCheckAdditionalContentSet()) + jsonSerializer.CheckAdditionalContent = true; + + using (var reader = new JsonTextReader(new StringReader(value))) + { + return jsonSerializer.Deserialize(reader, type); + } + } + +#if !(NET20 || NET35 || PORTABLE40) + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// Deserialization will happen on a new thread. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.DeserializeObject(value))")] + public static Task DeserializeObjectAsync(string value) + { + return DeserializeObjectAsync(value, null); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type using . + /// Deserialization will happen on a new thread. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.DeserializeObject(value, settings))")] + public static Task DeserializeObjectAsync(string value, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => DeserializeObject(value, settings)); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// Deserialization will happen on a new thread. + /// + /// The JSON to deserialize. + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.DeserializeObject(value))")] + public static Task DeserializeObjectAsync(string value) + { + return DeserializeObjectAsync(value, null, null); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type using . + /// Deserialization will happen on a new thread. + /// + /// The JSON to deserialize. + /// The type of the object to deserialize to. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => JsonConvert.DeserializeObject(value, type, settings))")] + public static Task DeserializeObjectAsync(string value, Type type, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => DeserializeObject(value, type, settings)); + } +#endif + #endregion + + /// + /// Populates the object with values from the JSON string. + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + public static void PopulateObject(string value, object target) + { + PopulateObject(value, target, null); + } + + /// + /// Populates the object with values from the JSON string using . + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + public static void PopulateObject(string value, object target, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + + using (JsonReader jsonReader = new JsonTextReader(new StringReader(value))) + { + jsonSerializer.Populate(jsonReader, target); + + if (jsonReader.Read() && jsonReader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + } + } + +#if !(NET20 || NET35 || PORTABLE40) + /// + /// Asynchronously populates the object with values from the JSON string using . + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be used. + /// + /// + /// A task that represents the asynchronous populate operation. + /// + [ObsoleteAttribute("PopulateObjectAsync is obsolete. Use the Task.Factory.StartNew method to populate an object with JSON values asynchronously: Task.Factory.StartNew(() => JsonConvert.PopulateObject(value, target, settings))")] + public static Task PopulateObjectAsync(string value, object target, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => PopulateObject(value, target, settings)); + } +#endif + +#if !(PORTABLE40 || PORTABLE || NETFX_CORE) + /// + /// Serializes the XML node to a JSON string. + /// + /// The node to serialize. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node) + { + return SerializeXmlNode(node, Formatting.None); + } + + /// + /// Serializes the XML node to a JSON string using formatting. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node, Formatting formatting) + { + XmlNodeConverter converter = new XmlNodeConverter(); + + return SerializeObject(node, formatting, converter); + } + + /// + /// Serializes the XML node to a JSON string using formatting and omits the root object if is true. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// Omits writing the root object. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node, Formatting formatting, bool omitRootObject) + { + XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject }; + + return SerializeObject(node, formatting, converter); + } + + /// + /// Deserializes the XmlNode from a JSON string. + /// + /// The JSON string. + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value) + { + return DeserializeXmlNode(value, null); + } + + /// + /// Deserializes the XmlNode from a JSON string nested in a root elment specified by . + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName) + { + return DeserializeXmlNode(value, deserializeRootElementName, false); + } + + /// + /// Deserializes the XmlNode from a JSON string nested in a root elment specified by + /// and writes a .NET array attribute for collections. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// + /// A flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName, bool writeArrayAttribute) + { + XmlNodeConverter converter = new XmlNodeConverter(); + converter.DeserializeRootElementName = deserializeRootElementName; + converter.WriteArrayAttribute = writeArrayAttribute; + + return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), converter); + } +#endif + +#if !NET20 && !PORTABLE40 + /// + /// Serializes the to a JSON string. + /// + /// The node to convert to JSON. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node) + { + return SerializeXNode(node, Formatting.None); + } + + /// + /// Serializes the to a JSON string using formatting. + /// + /// The node to convert to JSON. + /// Indicates how the output is formatted. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node, Formatting formatting) + { + return SerializeXNode(node, formatting, false); + } + + /// + /// Serializes the to a JSON string using formatting and omits the root object if is true. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// Omits writing the root object. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node, Formatting formatting, bool omitRootObject) + { + XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject }; + + return SerializeObject(node, formatting, converter); + } + + /// + /// Deserializes the from a JSON string. + /// + /// The JSON string. + /// The deserialized XNode + public static XDocument DeserializeXNode(string value) + { + return DeserializeXNode(value, null); + } + + /// + /// Deserializes the from a JSON string nested in a root elment specified by . + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// The deserialized XNode + public static XDocument DeserializeXNode(string value, string deserializeRootElementName) + { + return DeserializeXNode(value, deserializeRootElementName, false); + } + + /// + /// Deserializes the from a JSON string nested in a root elment specified by + /// and writes a .NET array attribute for collections. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// + /// A flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// The deserialized XNode + public static XDocument DeserializeXNode(string value, string deserializeRootElementName, bool writeArrayAttribute) + { + XmlNodeConverter converter = new XmlNodeConverter(); + converter.DeserializeRootElementName = deserializeRootElementName; + converter.WriteArrayAttribute = writeArrayAttribute; + + return (XDocument)DeserializeObject(value, typeof(XDocument), converter); + } +#endif + } +} diff --git a/Libs/JsonNet/JsonConverter.cs b/Libs/JsonNet/JsonConverter.cs new file mode 100644 index 0000000..424b7ee --- /dev/null +++ b/Libs/JsonNet/JsonConverter.cs @@ -0,0 +1,93 @@ +#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 Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Schema; + +namespace Newtonsoft.Json +{ + /// + /// Converts an object to and from JSON. + /// + public abstract class JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public abstract void WriteJson(JsonWriter writer, object value, JsonSerializer serializer); + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public abstract object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer); + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public abstract bool CanConvert(Type objectType); + + /// + /// Gets the of the JSON produced by the JsonConverter. + /// + /// The of the JSON produced by the JsonConverter. + public virtual JsonSchema GetSchema() + { + return null; + } + + /// + /// Gets a value indicating whether this can read JSON. + /// + /// true if this can read JSON; otherwise, false. + public virtual bool CanRead + { + get { return true; } + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// true if this can write JSON; otherwise, false. + public virtual bool CanWrite + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonConverterAttribute.cs b/Libs/JsonNet/JsonConverterAttribute.cs new file mode 100644 index 0000000..f389780 --- /dev/null +++ b/Libs/JsonNet/JsonConverterAttribute.cs @@ -0,0 +1,78 @@ +#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 Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to use the specified when serializing the member or class. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Parameter, AllowMultiple = false)] + public sealed class JsonConverterAttribute : Attribute + { + private readonly Type _converterType; + + /// + /// Gets the type of the converter. + /// + /// The type of the converter. + public Type ConverterType + { + get { return _converterType; } + } + + /// + /// The parameter list to use when constructing the JsonConverter described by ConverterType. + /// If null, the default constructor is used. + /// + public object[] ConverterParameters { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the converter. + public JsonConverterAttribute(Type converterType) + { + if (converterType == null) + throw new ArgumentNullException("converterType"); + + _converterType = converterType; + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the converter. + /// Parameter list to use when constructing the JsonConverter. Can be null. + public JsonConverterAttribute(Type converterType, params object[] converterParameters) + : this(converterType) + { + ConverterParameters = converterParameters; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonConverterCollection.cs b/Libs/JsonNet/JsonConverterCollection.cs new file mode 100644 index 0000000..b2c0492 --- /dev/null +++ b/Libs/JsonNet/JsonConverterCollection.cs @@ -0,0 +1,39 @@ +#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 +{ + /// + /// Represents a collection of . + /// + public class JsonConverterCollection : Collection + { + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonDictionaryAttribute.cs b/Libs/JsonNet/JsonDictionaryAttribute.cs new file mode 100644 index 0000000..7ddf411 --- /dev/null +++ b/Libs/JsonNet/JsonDictionaryAttribute.cs @@ -0,0 +1,52 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the collection. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonDictionaryAttribute : JsonContainerAttribute + { + /// + /// Initializes a new instance of the class. + /// + public JsonDictionaryAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + public JsonDictionaryAttribute(string id) + : base(id) + { + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonException.cs b/Libs/JsonNet/JsonException.cs new file mode 100644 index 0000000..edf123e --- /dev/null +++ b/Libs/JsonNet/JsonException.cs @@ -0,0 +1,92 @@ +#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.Globalization; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs during Json serialization or deserialization. + /// +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + [Serializable] +#endif + public class JsonException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public JsonException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + public JsonException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + internal static JsonException Create(IJsonLineInfo lineInfo, string path, string message) + { + message = JsonPosition.FormatMessage(lineInfo, path, message); + + return new JsonException(message); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonExtensionDataAttribute.cs b/Libs/JsonNet/JsonExtensionDataAttribute.cs new file mode 100644 index 0000000..6bc15b7 --- /dev/null +++ b/Libs/JsonNet/JsonExtensionDataAttribute.cs @@ -0,0 +1,37 @@ +using System; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to deserialize properties with no matching class member into the specified collection + /// and write values during serialization. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public class JsonExtensionDataAttribute : Attribute + { + /// + /// Gets or sets a value that indicates whether to write extension data when serializing the object. + /// + /// + /// true to write extension data when serializing the object; otherwise, false. The default is true. + /// + public bool WriteData { get; set; } + + /// + /// Gets or sets a value that indicates whether to read extension data when deserializing the object. + /// + /// + /// true to read extension data when deserializing the object; otherwise, false. The default is true. + /// + public bool ReadData { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonExtensionDataAttribute() + { + WriteData = true; + ReadData = true; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonIgnoreAttribute.cs b/Libs/JsonNet/JsonIgnoreAttribute.cs new file mode 100644 index 0000000..8229299 --- /dev/null +++ b/Libs/JsonNet/JsonIgnoreAttribute.cs @@ -0,0 +1,39 @@ +#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 +{ + /// + /// Instructs the not to serialize the public field or public read/write property value. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class JsonIgnoreAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonObjectAttribute.cs b/Libs/JsonNet/JsonObjectAttribute.cs new file mode 100644 index 0000000..61fad92 --- /dev/null +++ b/Libs/JsonNet/JsonObjectAttribute.cs @@ -0,0 +1,89 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the object. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonObjectAttribute : JsonContainerAttribute + { + private MemberSerialization _memberSerialization = MemberSerialization.OptOut; + + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal Required? _itemRequired; + + /// + /// Gets or sets the member serialization. + /// + /// The member serialization. + public MemberSerialization MemberSerialization + { + get { return _memberSerialization; } + set { _memberSerialization = value; } + } + + /// + /// Gets or sets a value that indicates whether the object's properties are required. + /// + /// + /// A value indicating whether the object's properties are required. + /// + public Required ItemRequired + { + get { return _itemRequired ?? default(Required); } + set { _itemRequired = value; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonObjectAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified member serialization. + /// + /// The member serialization. + public JsonObjectAttribute(MemberSerialization memberSerialization) + { + MemberSerialization = memberSerialization; + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + public JsonObjectAttribute(string id) + : base(id) + { + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonPosition.cs b/Libs/JsonNet/JsonPosition.cs new file mode 100644 index 0000000..7bd879e --- /dev/null +++ b/Libs/JsonNet/JsonPosition.cs @@ -0,0 +1,128 @@ +#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.Globalization; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + internal enum JsonContainerType + { + None, + Object, + Array, + Constructor + } + + internal struct JsonPosition + { + private static readonly char[] SpecialCharacters = {'.', ' ', '[', ']', '(', ')'}; + + internal JsonContainerType Type; + internal int Position; + internal string PropertyName; + internal bool HasIndex; + + public JsonPosition(JsonContainerType type) + { + Type = type; + HasIndex = TypeHasIndex(type); + Position = -1; + PropertyName = null; + } + + internal void WriteTo(StringBuilder sb) + { + switch (Type) + { + case JsonContainerType.Object: + if (sb.Length > 0) + sb.Append('.'); + + string propertyName = PropertyName; + if (propertyName.IndexOfAny(SpecialCharacters) != -1) + { + sb.Append(@"['"); + sb.Append(propertyName); + sb.Append(@"']"); + } + else + { + sb.Append(propertyName); + } + break; + case JsonContainerType.Array: + case JsonContainerType.Constructor: + sb.Append('['); + sb.Append(Position); + sb.Append(']'); + break; + } + } + + internal static bool TypeHasIndex(JsonContainerType type) + { + return (type == JsonContainerType.Array || type == JsonContainerType.Constructor); + } + + internal static string BuildPath(IEnumerable positions) + { + StringBuilder sb = new StringBuilder(); + + foreach (JsonPosition state in positions) + { + state.WriteTo(sb); + } + + return sb.ToString(); + } + + internal static string FormatMessage(IJsonLineInfo lineInfo, string path, string message) + { + // don't add a fullstop and space when message ends with a new line + if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal)) + { + message = message.Trim(); + + if (!message.EndsWith('.')) + message += "."; + + message += " "; + } + + message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path); + + if (lineInfo != null && lineInfo.HasLineInfo()) + message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition); + + message += "."; + + return message; + } + } +} diff --git a/Libs/JsonNet/JsonPropertyAttribute.cs b/Libs/JsonNet/JsonPropertyAttribute.cs new file mode 100644 index 0000000..5175f4c --- /dev/null +++ b/Libs/JsonNet/JsonPropertyAttribute.cs @@ -0,0 +1,201 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to always serialize the member with the specified name. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] + public sealed class JsonPropertyAttribute : Attribute + { + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal NullValueHandling? _nullValueHandling; + internal DefaultValueHandling? _defaultValueHandling; + internal ReferenceLoopHandling? _referenceLoopHandling; + internal ObjectCreationHandling? _objectCreationHandling; + internal TypeNameHandling? _typeNameHandling; + internal bool? _isReference; + internal int? _order; + internal Required? _required; + internal bool? _itemIsReference; + internal ReferenceLoopHandling? _itemReferenceLoopHandling; + internal TypeNameHandling? _itemTypeNameHandling; + + /// + /// Gets or sets the converter used when serializing the property's collection items. + /// + /// The collection's items converter. + public Type ItemConverterType { get; set; } + + /// + /// The parameter list to use when constructing the JsonConverter described by ItemConverterType. + /// If null, the default constructor is used. + /// When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number, + /// order, and type of these parameters. + /// + /// + /// [JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] + /// + public object[] ItemConverterParameters { get; set; } + + /// + /// Gets or sets the null value handling used when serializing this property. + /// + /// The null value handling. + public NullValueHandling NullValueHandling + { + get { return _nullValueHandling ?? default(NullValueHandling); } + set { _nullValueHandling = value; } + } + + /// + /// Gets or sets the default value handling used when serializing this property. + /// + /// The default value handling. + public DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling ?? default(DefaultValueHandling); } + set { _defaultValueHandling = value; } + } + + /// + /// Gets or sets the reference loop handling used when serializing this property. + /// + /// The reference loop handling. + public ReferenceLoopHandling ReferenceLoopHandling + { + get { return _referenceLoopHandling ?? default(ReferenceLoopHandling); } + set { _referenceLoopHandling = value; } + } + + /// + /// Gets or sets the object creation handling used when deserializing this property. + /// + /// The object creation handling. + public ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling ?? default(ObjectCreationHandling); } + set { _objectCreationHandling = value; } + } + + /// + /// Gets or sets the type name handling used when serializing this property. + /// + /// The type name handling. + public TypeNameHandling TypeNameHandling + { + get { return _typeNameHandling ?? default(TypeNameHandling); } + set { _typeNameHandling = value; } + } + + /// + /// Gets or sets whether this property's value is serialized as a reference. + /// + /// Whether this property's value is serialized as a reference. + public bool IsReference + { + get { return _isReference ?? default(bool); } + set { _isReference = value; } + } + + /// + /// Gets or sets the order of serialization and deserialization of a member. + /// + /// The numeric order of serialization or deserialization. + public int Order + { + get { return _order ?? default(int); } + set { _order = value; } + } + + /// + /// Gets or sets a value indicating whether this property is required. + /// + /// + /// A value indicating whether this property is required. + /// + public Required Required + { + get { return _required ?? Required.Default; } + set { _required = value; } + } + + /// + /// Gets or sets the name of the property. + /// + /// The name of the property. + public string PropertyName { get; set; } + + /// + /// Gets or sets the the reference loop handling used when serializing the property's collection items. + /// + /// The collection's items reference loop handling. + public ReferenceLoopHandling ItemReferenceLoopHandling + { + get { return _itemReferenceLoopHandling ?? default(ReferenceLoopHandling); } + set { _itemReferenceLoopHandling = value; } + } + + /// + /// Gets or sets the the type name handling used when serializing the property's collection items. + /// + /// The collection's items type name handling. + public TypeNameHandling ItemTypeNameHandling + { + get { return _itemTypeNameHandling ?? default(TypeNameHandling); } + set { _itemTypeNameHandling = value; } + } + + /// + /// Gets or sets whether this property's collection items are serialized as a reference. + /// + /// Whether this property's collection items are serialized as a reference. + public bool ItemIsReference + { + get { return _itemIsReference ?? default(bool); } + set { _itemIsReference = value; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonPropertyAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// Name of the property. + public JsonPropertyAttribute(string propertyName) + { + PropertyName = propertyName; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonReader.cs b/Libs/JsonNet/JsonReader.cs new file mode 100644 index 0000000..c14c4da --- /dev/null +++ b/Libs/JsonNet/JsonReader.cs @@ -0,0 +1,1040 @@ +#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.IO; +using System.Globalization; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public abstract class JsonReader : IDisposable + { + /// + /// Specifies the state of the reader. + /// + protected internal enum State + { + /// + /// The Read method has not been called. + /// + Start, + + /// + /// The end of the file has been reached successfully. + /// + Complete, + + /// + /// Reader is at a property. + /// + Property, + + /// + /// Reader is at the start of an object. + /// + ObjectStart, + + /// + /// Reader is in an object. + /// + Object, + + /// + /// Reader is at the start of an array. + /// + ArrayStart, + + /// + /// Reader is in an array. + /// + Array, + + /// + /// The Close method has been called. + /// + Closed, + + /// + /// Reader has just read a value. + /// + PostValue, + + /// + /// Reader is at the start of a constructor. + /// + ConstructorStart, + + /// + /// Reader in a constructor. + /// + Constructor, + + /// + /// An error occurred that prevents the read operation from continuing. + /// + Error, + + /// + /// The end of the file has been reached successfully. + /// + Finished + } + + // current Token data + private JsonToken _tokenType; + private object _value; + internal char _quoteChar; + internal State _currentState; + internal ReadType _readType; + private JsonPosition _currentPosition; + private CultureInfo _culture; + private DateTimeZoneHandling _dateTimeZoneHandling; + private int? _maxDepth; + private bool _hasExceededMaxDepth; + internal DateParseHandling _dateParseHandling; + internal FloatParseHandling _floatParseHandling; + private string _dateFormatString; + private readonly List _stack; + + /// + /// Gets the current reader state. + /// + /// The current reader state. + protected State CurrentState + { + get { return _currentState; } + } + + /// + /// Gets or sets a value indicating whether the underlying stream or + /// should be closed when the reader is closed. + /// + /// + /// true to close the underlying stream or when + /// the reader is closed; otherwise false. The default is true. + /// + public bool CloseInput { get; set; } + + /// + /// Gets or sets a value indicating whether multiple pieces of JSON content can + /// be read from a continuous stream without erroring. + /// + /// + /// true to support reading multiple pieces of JSON content; otherwise false. The default is false. + /// + public bool SupportMultipleContent { get; set; } + + /// + /// Gets the quotation mark character used to enclose the value of a string. + /// + public virtual char QuoteChar + { + get { return _quoteChar; } + protected internal set { _quoteChar = value; } + } + + /// + /// Get or set how time zones are handling when reading JSON. + /// + public DateTimeZoneHandling DateTimeZoneHandling + { + get { return _dateTimeZoneHandling; } + set { _dateTimeZoneHandling = value; } + } + + /// + /// Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + /// + public DateParseHandling DateParseHandling + { + get { return _dateParseHandling; } + set { _dateParseHandling = value; } + } + + /// + /// Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + /// + public FloatParseHandling FloatParseHandling + { + get { return _floatParseHandling; } + set { _floatParseHandling = value; } + } + + /// + /// Get or set how custom date formatted strings are parsed when reading JSON. + /// + public string DateFormatString + { + get { return _dateFormatString; } + set { _dateFormatString = value; } + } + + /// + /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + /// + public int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + } + } + + /// + /// Gets the type of the current JSON token. + /// + public virtual JsonToken TokenType + { + get { return _tokenType; } + } + + /// + /// Gets the text value of the current JSON token. + /// + public virtual object Value + { + get { return _value; } + } + + /// + /// Gets The Common Language Runtime (CLR) type for the current JSON token. + /// + public virtual Type ValueType + { + get { return (_value != null) ? _value.GetType() : null; } + } + + /// + /// Gets the depth of the current token in the JSON document. + /// + /// The depth of the current token in the JSON document. + public virtual int Depth + { + get + { + int depth = _stack.Count; + if (IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None) + return depth; + else + return depth + 1; + } + } + + /// + /// Gets the path of the current JSON token. + /// + public virtual string Path + { + get + { + if (_currentPosition.Type == JsonContainerType.None) + return string.Empty; + + bool insideContainer = (_currentState != State.ArrayStart + && _currentState != State.ConstructorStart + && _currentState != State.ObjectStart); + + IEnumerable positions = (!insideContainer) + ? _stack + : _stack.Concat(new[] { _currentPosition }); + + return JsonPosition.BuildPath(positions); + } + } + + /// + /// Gets or sets the culture used when reading JSON. Defaults to . + /// + public CultureInfo Culture + { + get { return _culture ?? CultureInfo.InvariantCulture; } + set { _culture = value; } + } + + internal JsonPosition GetPosition(int depth) + { + if (depth < _stack.Count) + return _stack[depth]; + + return _currentPosition; + } + + /// + /// Initializes a new instance of the class with the specified . + /// + protected JsonReader() + { + _currentState = State.Start; + _stack = new List(4); + _dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + _dateParseHandling = DateParseHandling.DateTime; + _floatParseHandling = FloatParseHandling.Double; + + CloseInput = true; + } + + private void Push(JsonContainerType value) + { + UpdateScopeWithFinishedValue(); + + if (_currentPosition.Type == JsonContainerType.None) + { + _currentPosition = new JsonPosition(value); + } + else + { + _stack.Add(_currentPosition); + _currentPosition = new JsonPosition(value); + + // this is a little hacky because Depth increases when first property/value is written but only testing here is faster/simpler + if (_maxDepth != null && Depth + 1 > _maxDepth && !_hasExceededMaxDepth) + { + _hasExceededMaxDepth = true; + throw JsonReaderException.Create(this, "The reader's MaxDepth of {0} has been exceeded.".FormatWith(CultureInfo.InvariantCulture, _maxDepth)); + } + } + } + + private JsonContainerType Pop() + { + JsonPosition oldPosition; + if (_stack.Count > 0) + { + oldPosition = _currentPosition; + _currentPosition = _stack[_stack.Count - 1]; + _stack.RemoveAt(_stack.Count - 1); + } + else + { + oldPosition = _currentPosition; + _currentPosition = new JsonPosition(); + } + + if (_maxDepth != null && Depth <= _maxDepth) + _hasExceededMaxDepth = false; + + return oldPosition.Type; + } + + private JsonContainerType Peek() + { + return _currentPosition.Type; + } + + /// + /// Reads the next JSON token from the stream. + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + public abstract bool Read(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public abstract int? ReadAsInt32(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public abstract string ReadAsString(); + + /// + /// Reads the next JSON token from the stream as a []. + /// + /// A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. + public abstract byte[] ReadAsBytes(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public abstract decimal? ReadAsDecimal(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public abstract DateTime? ReadAsDateTime(); + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public abstract DateTimeOffset? ReadAsDateTimeOffset(); +#endif + + internal virtual bool ReadInternal() + { + throw new NotImplementedException(); + } + +#if !NET20 + internal DateTimeOffset? ReadAsDateTimeOffsetInternal() + { + _readType = ReadType.ReadAsDateTimeOffset; + + JsonToken t; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + else + { + t = TokenType; + } + } while (t == JsonToken.Comment); + + if (t == JsonToken.Date) + { + if (Value is DateTime) + SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value), false); + + return (DateTimeOffset)Value; + } + + if (t == JsonToken.Null) + return null; + + if (t == JsonToken.String) + { + string s = (string)Value; + if (string.IsNullOrEmpty(s)) + { + SetToken(JsonToken.Null); + return null; + } + + object temp; + DateTimeOffset dt; + if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTimeOffset, DateTimeZoneHandling, _dateFormatString, Culture, out temp)) + { + dt = (DateTimeOffset)temp; + SetToken(JsonToken.Date, dt, false); + return dt; + } + + if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) + { + SetToken(JsonToken.Date, dt, false); + return dt; + } + + throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); + } + + if (t == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t)); + } +#endif + + internal byte[] ReadAsBytesInternal() + { + _readType = ReadType.ReadAsBytes; + + JsonToken t; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + else + { + t = TokenType; + } + } while (t == JsonToken.Comment); + + if (IsWrappedInTypeObject()) + { + byte[] data = ReadAsBytes(); + ReadInternal(); + SetToken(JsonToken.Bytes, data, false); + return data; + } + + // attempt to convert possible base 64 string to bytes + if (t == JsonToken.String) + { + string s = (string)Value; + + byte[] data; + + Guid g; + if (s.Length == 0) + { + data = new byte[0]; + } + else if (ConvertUtils.TryConvertGuid(s, out g)) + { + data = g.ToByteArray(); + } + else + { + data = Convert.FromBase64String(s); + } + + SetToken(JsonToken.Bytes, data, false); + return data; + } + + if (t == JsonToken.Null) + return null; + + if (t == JsonToken.Bytes) + { + if (ValueType == typeof(Guid)) + { + byte[] data = ((Guid)Value).ToByteArray(); + SetToken(JsonToken.Bytes, data, false); + return data; + } + + return (byte[])Value; + } + + if (t == JsonToken.StartArray) + { + List data = new List(); + + while (ReadInternal()) + { + t = TokenType; + switch (t) + { + case JsonToken.Integer: + data.Add(Convert.ToByte(Value, CultureInfo.InvariantCulture)); + break; + case JsonToken.EndArray: + byte[] d = data.ToArray(); + SetToken(JsonToken.Bytes, d, false); + return d; + case JsonToken.Comment: + // skip + break; + default: + throw JsonReaderException.Create(this, "Unexpected token when reading bytes: {0}.".FormatWith(CultureInfo.InvariantCulture, t)); + } + } + + throw JsonReaderException.Create(this, "Unexpected end when reading bytes."); + } + + if (t == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t)); + } + + internal decimal? ReadAsDecimalInternal() + { + _readType = ReadType.ReadAsDecimal; + + JsonToken t; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + else + { + t = TokenType; + } + } while (t == JsonToken.Comment); + + if (t == JsonToken.Integer || t == JsonToken.Float) + { + if (!(Value is decimal)) + SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture), false); + + return (decimal)Value; + } + + if (t == JsonToken.Null) + return null; + + if (t == JsonToken.String) + { + string s = (string)Value; + if (string.IsNullOrEmpty(s)) + { + SetToken(JsonToken.Null); + return null; + } + + decimal d; + if (decimal.TryParse(s, NumberStyles.Number, Culture, out d)) + { + SetToken(JsonToken.Float, d, false); + return d; + } + else + { + throw JsonReaderException.Create(this, "Could not convert string to decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); + } + } + + if (t == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading decimal. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t)); + } + + internal int? ReadAsInt32Internal() + { + _readType = ReadType.ReadAsInt32; + + JsonToken t; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + else + { + t = TokenType; + } + } while (t == JsonToken.Comment); + + if (t == JsonToken.Integer || t == JsonToken.Float) + { + if (!(Value is int)) + SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture), false); + + return (int)Value; + } + + if (t == JsonToken.Null) + return null; + + int i; + if (t == JsonToken.String) + { + string s = (string)Value; + if (string.IsNullOrEmpty(s)) + { + SetToken(JsonToken.Null); + return null; + } + + if (int.TryParse(s, NumberStyles.Integer, Culture, out i)) + { + SetToken(JsonToken.Integer, i, false); + return i; + } + else + { + throw JsonReaderException.Create(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); + } + } + + if (t == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading integer. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + + internal string ReadAsStringInternal() + { + _readType = ReadType.ReadAsString; + + JsonToken t; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + else + { + t = TokenType; + } + } while (t == JsonToken.Comment); + + if (t == JsonToken.String) + return (string)Value; + + if (t == JsonToken.Null) + return null; + + if (IsPrimitiveToken(t)) + { + if (Value != null) + { + string s; + if (Value is IFormattable) + s = ((IFormattable)Value).ToString(null, Culture); + else + s = Value.ToString(); + + SetToken(JsonToken.String, s, false); + return s; + } + } + + if (t == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading string. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, t)); + } + + internal DateTime? ReadAsDateTimeInternal() + { + _readType = ReadType.ReadAsDateTime; + + do + { + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return null; + } + } while (TokenType == JsonToken.Comment); + + if (TokenType == JsonToken.Date) + return (DateTime)Value; + + if (TokenType == JsonToken.Null) + return null; + + if (TokenType == JsonToken.String) + { + string s = (string)Value; + if (string.IsNullOrEmpty(s)) + { + SetToken(JsonToken.Null); + return null; + } + + DateTime dt; + object temp; + if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTime, DateTimeZoneHandling, _dateFormatString, Culture, out temp)) + { + dt = (DateTime)temp; + dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); + SetToken(JsonToken.Date, dt, false); + return dt; + } + + if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt)) + { + dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling); + SetToken(JsonToken.Date, dt, false); + return dt; + } + + throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); + } + + if (TokenType == JsonToken.EndArray) + return null; + + throw JsonReaderException.Create(this, "Error reading date. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + + private bool IsWrappedInTypeObject() + { + _readType = ReadType.Read; + + if (TokenType == JsonToken.StartObject) + { + if (!ReadInternal()) + throw JsonReaderException.Create(this, "Unexpected end when reading bytes."); + + if (Value.ToString() == JsonTypeReflector.TypePropertyName) + { + ReadInternal(); + if (Value != null && Value.ToString().StartsWith("System.Byte[]", StringComparison.Ordinal)) + { + ReadInternal(); + if (Value.ToString() == JsonTypeReflector.ValuePropertyName) + { + return true; + } + } + } + + throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, JsonToken.StartObject)); + } + + return false; + } + + /// + /// Skips the children of the current token. + /// + public void Skip() + { + if (TokenType == JsonToken.PropertyName) + Read(); + + if (IsStartToken(TokenType)) + { + int depth = Depth; + + while (Read() && (depth < Depth)) + { + } + } + } + + /// + /// Sets the current token. + /// + /// The new token. + protected void SetToken(JsonToken newToken) + { + SetToken(newToken, null, true); + } + + /// + /// Sets the current token and value. + /// + /// The new token. + /// The value. + protected void SetToken(JsonToken newToken, object value) + { + SetToken(newToken, value, true); + } + + internal void SetToken(JsonToken newToken, object value, bool updateIndex) + { + _tokenType = newToken; + _value = value; + + switch (newToken) + { + case JsonToken.StartObject: + _currentState = State.ObjectStart; + Push(JsonContainerType.Object); + break; + case JsonToken.StartArray: + _currentState = State.ArrayStart; + Push(JsonContainerType.Array); + break; + case JsonToken.StartConstructor: + _currentState = State.ConstructorStart; + Push(JsonContainerType.Constructor); + break; + case JsonToken.EndObject: + ValidateEnd(JsonToken.EndObject); + break; + case JsonToken.EndArray: + ValidateEnd(JsonToken.EndArray); + break; + case JsonToken.EndConstructor: + ValidateEnd(JsonToken.EndConstructor); + break; + case JsonToken.PropertyName: + _currentState = State.Property; + + _currentPosition.PropertyName = (string)value; + break; + case JsonToken.Undefined: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.String: + case JsonToken.Raw: + case JsonToken.Bytes: + SetPostValueState(updateIndex); + break; + } + } + + internal void SetPostValueState(bool updateIndex) + { + if (Peek() != JsonContainerType.None) + _currentState = State.PostValue; + else + SetFinished(); + + if (updateIndex) + UpdateScopeWithFinishedValue(); + } + + private void UpdateScopeWithFinishedValue() + { + if (_currentPosition.HasIndex) + _currentPosition.Position++; + } + + private void ValidateEnd(JsonToken endToken) + { + JsonContainerType currentObject = Pop(); + + if (GetTypeForCloseToken(endToken) != currentObject) + throw JsonReaderException.Create(this, "JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject)); + + if (Peek() != JsonContainerType.None) + _currentState = State.PostValue; + else + SetFinished(); + } + + /// + /// Sets the state based on current token type. + /// + protected void SetStateBasedOnCurrent() + { + JsonContainerType currentObject = Peek(); + + switch (currentObject) + { + case JsonContainerType.Object: + _currentState = State.Object; + break; + case JsonContainerType.Array: + _currentState = State.Array; + break; + case JsonContainerType.Constructor: + _currentState = State.Constructor; + break; + case JsonContainerType.None: + SetFinished(); + break; + default: + throw JsonReaderException.Create(this, "While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject)); + } + } + + private void SetFinished() + { + if (SupportMultipleContent) + _currentState = State.Start; + else + _currentState = State.Finished; + } + + internal static bool IsPrimitiveToken(JsonToken token) + { + switch (token) + { + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.Bytes: + return true; + default: + return false; + } + } + + internal static bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + return true; + default: + return false; + } + } + + private JsonContainerType GetTypeForCloseToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + return JsonContainerType.Object; + case JsonToken.EndArray: + return JsonContainerType.Array; + case JsonToken.EndConstructor: + return JsonContainerType.Constructor; + default: + throw JsonReaderException.Create(this, "Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + void IDisposable.Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_currentState != State.Closed && disposing) + Close(); + } + + /// + /// Changes the to Closed. + /// + public virtual void Close() + { + _currentState = State.Closed; + _tokenType = JsonToken.None; + _value = null; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonReaderException.cs b/Libs/JsonNet/JsonReaderException.cs new file mode 100644 index 0000000..034a7e5 --- /dev/null +++ b/Libs/JsonNet/JsonReaderException.cs @@ -0,0 +1,139 @@ +#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.Globalization; +using System.Runtime.Serialization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs while reading Json text. + /// +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + [Serializable] +#endif + public class JsonReaderException : JsonException + { + /// + /// Gets the line number indicating where the error occurred. + /// + /// The line number indicating where the error occurred. + public int LineNumber { get; private set; } + + /// + /// Gets the line position indicating where the error occurred. + /// + /// The line position indicating where the error occurred. + public int LinePosition { get; private set; } + + /// + /// Gets the path to the JSON where the error occurred. + /// + /// The path to the JSON where the error occurred. + public string Path { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonReaderException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonReaderException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonReaderException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + public JsonReaderException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + internal JsonReaderException(string message, Exception innerException, string path, int lineNumber, int linePosition) + : base(message, innerException) + { + Path = path; + LineNumber = lineNumber; + LinePosition = linePosition; + } + + internal static JsonReaderException Create(JsonReader reader, string message) + { + return Create(reader, message, null); + } + + internal static JsonReaderException Create(JsonReader reader, string message, Exception ex) + { + return Create(reader as IJsonLineInfo, reader.Path, message, ex); + } + + internal static JsonReaderException Create(IJsonLineInfo lineInfo, string path, string message, Exception ex) + { + message = JsonPosition.FormatMessage(lineInfo, path, message); + + int lineNumber; + int linePosition; + if (lineInfo != null && lineInfo.HasLineInfo()) + { + lineNumber = lineInfo.LineNumber; + linePosition = lineInfo.LinePosition; + } + else + { + lineNumber = 0; + linePosition = 0; + } + + return new JsonReaderException(message, ex, path, lineNumber, linePosition); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonSerializationException.cs b/Libs/JsonNet/JsonSerializationException.cs new file mode 100644 index 0000000..2c1099f --- /dev/null +++ b/Libs/JsonNet/JsonSerializationException.cs @@ -0,0 +1,100 @@ +#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.Runtime.Serialization; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs during Json serialization or deserialization. + /// +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + [Serializable] +#endif + public class JsonSerializationException : JsonException + { + /// + /// Initializes a new instance of the class. + /// + public JsonSerializationException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonSerializationException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonSerializationException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + public JsonSerializationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + internal static JsonSerializationException Create(JsonReader reader, string message) + { + return Create(reader, message, null); + } + + internal static JsonSerializationException Create(JsonReader reader, string message, Exception ex) + { + return Create(reader as IJsonLineInfo, reader.Path, message, ex); + } + + internal static JsonSerializationException Create(IJsonLineInfo lineInfo, string path, string message, Exception ex) + { + message = JsonPosition.FormatMessage(lineInfo, path, message); + + return new JsonSerializationException(message, ex); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonSerializer.cs b/Libs/JsonNet/JsonSerializer.cs new file mode 100644 index 0000000..10c4297 --- /dev/null +++ b/Libs/JsonNet/JsonSerializer.cs @@ -0,0 +1,987 @@ +#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.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; +using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; + +namespace Newtonsoft.Json +{ + /// + /// Serializes and deserializes objects into and from the JSON format. + /// The enables you to control how objects are encoded into JSON. + /// + public class JsonSerializer + { + internal TypeNameHandling _typeNameHandling; + internal FormatterAssemblyStyle _typeNameAssemblyFormat; + internal PreserveReferencesHandling _preserveReferencesHandling; + internal ReferenceLoopHandling _referenceLoopHandling; + internal MissingMemberHandling _missingMemberHandling; + internal ObjectCreationHandling _objectCreationHandling; + internal NullValueHandling _nullValueHandling; + internal DefaultValueHandling _defaultValueHandling; + internal ConstructorHandling _constructorHandling; + internal MetadataPropertyHandling _metadataPropertyHandling; + internal JsonConverterCollection _converters; + internal IContractResolver _contractResolver; + internal ITraceWriter _traceWriter; + internal SerializationBinder _binder; + internal StreamingContext _context; + private IReferenceResolver _referenceResolver; + + private Formatting? _formatting; + private DateFormatHandling? _dateFormatHandling; + private DateTimeZoneHandling? _dateTimeZoneHandling; + private DateParseHandling? _dateParseHandling; + private FloatFormatHandling? _floatFormatHandling; + private FloatParseHandling? _floatParseHandling; + private StringEscapeHandling? _stringEscapeHandling; + private CultureInfo _culture; + private int? _maxDepth; + private bool _maxDepthSet; + private bool? _checkAdditionalContent; + private string _dateFormatString; + private bool _dateFormatStringSet; + + /// + /// Occurs when the errors during serialization and deserialization. + /// + public virtual event EventHandler Error; + + /// + /// Gets or sets the used by the serializer when resolving references. + /// + public virtual IReferenceResolver ReferenceResolver + { + get { return GetReferenceResolver(); } + set + { + if (value == null) + throw new ArgumentNullException("value", "Reference resolver cannot be null."); + + _referenceResolver = value; + } + } + + /// + /// Gets or sets the used by the serializer when resolving type names. + /// + public virtual SerializationBinder Binder + { + get { return _binder; } + set + { + if (value == null) + throw new ArgumentNullException("value", "Serialization binder cannot be null."); + + _binder = value; + } + } + + /// + /// Gets or sets the used by the serializer when writing trace messages. + /// + /// The trace writer. + public virtual ITraceWriter TraceWriter + { + get { return _traceWriter; } + set { _traceWriter = value; } + } + + /// + /// Gets or sets how type name writing and reading is handled by the serializer. + /// + public virtual TypeNameHandling TypeNameHandling + { + get { return _typeNameHandling; } + set + { + if (value < TypeNameHandling.None || value > TypeNameHandling.Auto) + throw new ArgumentOutOfRangeException("value"); + + _typeNameHandling = value; + } + } + + /// + /// Gets or sets how a type name assembly is written and resolved by the serializer. + /// + /// The type name assembly format. + public virtual FormatterAssemblyStyle TypeNameAssemblyFormat + { + get { return _typeNameAssemblyFormat; } + set + { + if (value < FormatterAssemblyStyle.Simple || value > FormatterAssemblyStyle.Full) + throw new ArgumentOutOfRangeException("value"); + + _typeNameAssemblyFormat = value; + } + } + + /// + /// Gets or sets how object references are preserved by the serializer. + /// + public virtual PreserveReferencesHandling PreserveReferencesHandling + { + get { return _preserveReferencesHandling; } + set + { + if (value < PreserveReferencesHandling.None || value > PreserveReferencesHandling.All) + throw new ArgumentOutOfRangeException("value"); + + _preserveReferencesHandling = value; + } + } + + /// + /// Get or set how reference loops (e.g. a class referencing itself) is handled. + /// + public virtual ReferenceLoopHandling ReferenceLoopHandling + { + get { return _referenceLoopHandling; } + set + { + if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize) + throw new ArgumentOutOfRangeException("value"); + + _referenceLoopHandling = value; + } + } + + /// + /// Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + /// + public virtual MissingMemberHandling MissingMemberHandling + { + get { return _missingMemberHandling; } + set + { + if (value < MissingMemberHandling.Ignore || value > MissingMemberHandling.Error) + throw new ArgumentOutOfRangeException("value"); + + _missingMemberHandling = value; + } + } + + /// + /// Get or set how null values are handled during serialization and deserialization. + /// + public virtual NullValueHandling NullValueHandling + { + get { return _nullValueHandling; } + set + { + if (value < NullValueHandling.Include || value > NullValueHandling.Ignore) + throw new ArgumentOutOfRangeException("value"); + + _nullValueHandling = value; + } + } + + /// + /// Get or set how null default are handled during serialization and deserialization. + /// + public virtual DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling; } + set + { + if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAndPopulate) + throw new ArgumentOutOfRangeException("value"); + + _defaultValueHandling = value; + } + } + + /// + /// Gets or sets how objects are created during deserialization. + /// + /// The object creation handling. + public virtual ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling; } + set + { + if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace) + throw new ArgumentOutOfRangeException("value"); + + _objectCreationHandling = value; + } + } + + /// + /// Gets or sets how constructors are used during deserialization. + /// + /// The constructor handling. + public virtual ConstructorHandling ConstructorHandling + { + get { return _constructorHandling; } + set + { + if (value < ConstructorHandling.Default || value > ConstructorHandling.AllowNonPublicDefaultConstructor) + throw new ArgumentOutOfRangeException("value"); + + _constructorHandling = value; + } + } + + /// + /// Gets or sets how metadata properties are used during deserialization. + /// + /// The metadata properties handling. + public virtual MetadataPropertyHandling MetadataPropertyHandling + { + get { return _metadataPropertyHandling; } + set + { + if (value < MetadataPropertyHandling.Default || value > MetadataPropertyHandling.Ignore) + throw new ArgumentOutOfRangeException("value"); + + _metadataPropertyHandling = value; + } + } + + /// + /// Gets a collection that will be used during serialization. + /// + /// Collection that will be used during serialization. + public virtual JsonConverterCollection Converters + { + get + { + if (_converters == null) + _converters = new JsonConverterCollection(); + + return _converters; + } + } + + /// + /// Gets or sets the contract resolver used by the serializer when + /// serializing .NET objects to JSON and vice versa. + /// + public virtual IContractResolver ContractResolver + { + get { return _contractResolver; } + set { _contractResolver = value ?? DefaultContractResolver.Instance; } + } + + /// + /// Gets or sets the used by the serializer when invoking serialization callback methods. + /// + /// The context. + public virtual StreamingContext Context + { + get { return _context; } + set { _context = value; } + } + + /// + /// Indicates how JSON text output is formatted. + /// + public virtual Formatting Formatting + { + get { return _formatting ?? JsonSerializerSettings.DefaultFormatting; } + set { _formatting = value; } + } + + /// + /// Get or set how dates are written to JSON text. + /// + public virtual DateFormatHandling DateFormatHandling + { + get { return _dateFormatHandling ?? JsonSerializerSettings.DefaultDateFormatHandling; } + set { _dateFormatHandling = value; } + } + + /// + /// Get or set how time zones are handling during serialization and deserialization. + /// + public virtual DateTimeZoneHandling DateTimeZoneHandling + { + get { return _dateTimeZoneHandling ?? JsonSerializerSettings.DefaultDateTimeZoneHandling; } + set { _dateTimeZoneHandling = value; } + } + + /// + /// Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + /// + public virtual DateParseHandling DateParseHandling + { + get { return _dateParseHandling ?? JsonSerializerSettings.DefaultDateParseHandling; } + set { _dateParseHandling = value; } + } + + /// + /// Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + /// + public virtual FloatParseHandling FloatParseHandling + { + get { return _floatParseHandling ?? JsonSerializerSettings.DefaultFloatParseHandling; } + set { _floatParseHandling = value; } + } + + /// + /// Get or set how special floating point numbers, e.g. , + /// and , + /// are written as JSON text. + /// + public virtual FloatFormatHandling FloatFormatHandling + { + get { return _floatFormatHandling ?? JsonSerializerSettings.DefaultFloatFormatHandling; } + set { _floatFormatHandling = value; } + } + + /// + /// Get or set how strings are escaped when writing JSON text. + /// + public virtual StringEscapeHandling StringEscapeHandling + { + get { return _stringEscapeHandling ?? JsonSerializerSettings.DefaultStringEscapeHandling; } + set { _stringEscapeHandling = value; } + } + + /// + /// Get or set how and values are formatting when writing JSON text. + /// + public virtual string DateFormatString + { + get { return _dateFormatString ?? JsonSerializerSettings.DefaultDateFormatString; } + set + { + _dateFormatString = value; + _dateFormatStringSet = true; + } + } + + /// + /// Gets or sets the culture used when reading JSON. Defaults to . + /// + public virtual CultureInfo Culture + { + get { return _culture ?? JsonSerializerSettings.DefaultCulture; } + set { _culture = value; } + } + + /// + /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + /// + public virtual int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + _maxDepthSet = true; + } + } + + /// + /// Gets a value indicating whether there will be a check for additional JSON content after deserializing an object. + /// + /// + /// true if there will be a check for additional JSON content after deserializing an object; otherwise, false. + /// + public virtual bool CheckAdditionalContent + { + get { return _checkAdditionalContent ?? JsonSerializerSettings.DefaultCheckAdditionalContent; } + set { _checkAdditionalContent = value; } + } + + internal bool IsCheckAdditionalContentSet() + { + return (_checkAdditionalContent != null); + } + + /// + /// Initializes a new instance of the class. + /// + public JsonSerializer() + { + _referenceLoopHandling = JsonSerializerSettings.DefaultReferenceLoopHandling; + _missingMemberHandling = JsonSerializerSettings.DefaultMissingMemberHandling; + _nullValueHandling = JsonSerializerSettings.DefaultNullValueHandling; + _defaultValueHandling = JsonSerializerSettings.DefaultDefaultValueHandling; + _objectCreationHandling = JsonSerializerSettings.DefaultObjectCreationHandling; + _preserveReferencesHandling = JsonSerializerSettings.DefaultPreserveReferencesHandling; + _constructorHandling = JsonSerializerSettings.DefaultConstructorHandling; + _typeNameHandling = JsonSerializerSettings.DefaultTypeNameHandling; + _metadataPropertyHandling = JsonSerializerSettings.DefaultMetadataPropertyHandling; + _context = JsonSerializerSettings.DefaultContext; + _binder = DefaultSerializationBinder.Instance; + + _culture = JsonSerializerSettings.DefaultCulture; + _contractResolver = DefaultContractResolver.Instance; + } + + /// + /// Creates a new instance. + /// The will not use default settings. + /// + /// + /// A new instance. + /// The will not use default settings. + /// + public static JsonSerializer Create() + { + return new JsonSerializer(); + } + + /// + /// Creates a new instance using the specified . + /// The will not use default settings. + /// + /// The settings to be applied to the . + /// + /// A new instance using the specified . + /// The will not use default settings. + /// + public static JsonSerializer Create(JsonSerializerSettings settings) + { + JsonSerializer serializer = Create(); + + if (settings != null) + ApplySerializerSettings(serializer, settings); + + return serializer; + } + + /// + /// Creates a new instance. + /// The will use default settings. + /// + /// + /// A new instance. + /// The will use default settings. + /// + public static JsonSerializer CreateDefault() + { + // copy static to local variable to avoid concurrency issues + Func defaultSettingsCreator = JsonConvert.DefaultSettings; + JsonSerializerSettings defaultSettings = (defaultSettingsCreator != null) ? defaultSettingsCreator() : null; + + return Create(defaultSettings); + } + + /// + /// Creates a new instance using the specified . + /// The will use default settings. + /// + /// The settings to be applied to the . + /// + /// A new instance using the specified . + /// The will use default settings. + /// + public static JsonSerializer CreateDefault(JsonSerializerSettings settings) + { + JsonSerializer serializer = CreateDefault(); + if (settings != null) + ApplySerializerSettings(serializer, settings); + + return serializer; + } + + private static void ApplySerializerSettings(JsonSerializer serializer, JsonSerializerSettings settings) + { + if (!CollectionUtils.IsNullOrEmpty(settings.Converters)) + { + // insert settings converters at the beginning so they take precedence + // if user wants to remove one of the default converters they will have to do it manually + for (int i = 0; i < settings.Converters.Count; i++) + { + serializer.Converters.Insert(i, settings.Converters[i]); + } + } + + // serializer specific + if (settings._typeNameHandling != null) + serializer.TypeNameHandling = settings.TypeNameHandling; + if (settings._metadataPropertyHandling != null) + serializer.MetadataPropertyHandling = settings.MetadataPropertyHandling; + if (settings._typeNameAssemblyFormat != null) + serializer.TypeNameAssemblyFormat = settings.TypeNameAssemblyFormat; + if (settings._preserveReferencesHandling != null) + serializer.PreserveReferencesHandling = settings.PreserveReferencesHandling; + if (settings._referenceLoopHandling != null) + serializer.ReferenceLoopHandling = settings.ReferenceLoopHandling; + if (settings._missingMemberHandling != null) + serializer.MissingMemberHandling = settings.MissingMemberHandling; + if (settings._objectCreationHandling != null) + serializer.ObjectCreationHandling = settings.ObjectCreationHandling; + if (settings._nullValueHandling != null) + serializer.NullValueHandling = settings.NullValueHandling; + if (settings._defaultValueHandling != null) + serializer.DefaultValueHandling = settings.DefaultValueHandling; + if (settings._constructorHandling != null) + serializer.ConstructorHandling = settings.ConstructorHandling; + if (settings._context != null) + serializer.Context = settings.Context; + if (settings._checkAdditionalContent != null) + serializer._checkAdditionalContent = settings._checkAdditionalContent; + + if (settings.Error != null) + serializer.Error += settings.Error; + + if (settings.ContractResolver != null) + serializer.ContractResolver = settings.ContractResolver; + if (settings.ReferenceResolver != null) + serializer.ReferenceResolver = settings.ReferenceResolver; + if (settings.TraceWriter != null) + serializer.TraceWriter = settings.TraceWriter; + if (settings.Binder != null) + serializer.Binder = settings.Binder; + + // reader/writer specific + // unset values won't override reader/writer set values + if (settings._formatting != null) + serializer._formatting = settings._formatting; + if (settings._dateFormatHandling != null) + serializer._dateFormatHandling = settings._dateFormatHandling; + if (settings._dateTimeZoneHandling != null) + serializer._dateTimeZoneHandling = settings._dateTimeZoneHandling; + if (settings._dateParseHandling != null) + serializer._dateParseHandling = settings._dateParseHandling; + if (settings._dateFormatStringSet) + { + serializer._dateFormatString = settings._dateFormatString; + serializer._dateFormatStringSet = settings._dateFormatStringSet; + } + if (settings._floatFormatHandling != null) + serializer._floatFormatHandling = settings._floatFormatHandling; + if (settings._floatParseHandling != null) + serializer._floatParseHandling = settings._floatParseHandling; + if (settings._stringEscapeHandling != null) + serializer._stringEscapeHandling = settings._stringEscapeHandling; + if (settings._culture != null) + serializer._culture = settings._culture; + if (settings._maxDepthSet) + { + serializer._maxDepth = settings._maxDepth; + serializer._maxDepthSet = settings._maxDepthSet; + } + } + + /// + /// Populates the JSON values onto the target object. + /// + /// The that contains the JSON structure to reader values from. + /// The target object to populate values onto. + public void Populate(TextReader reader, object target) + { + Populate(new JsonTextReader(reader), target); + } + + /// + /// Populates the JSON values onto the target object. + /// + /// The that contains the JSON structure to reader values from. + /// The target object to populate values onto. + public void Populate(JsonReader reader, object target) + { + PopulateInternal(reader, target); + } + + internal virtual void PopulateInternal(JsonReader reader, object target) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + ValidationUtils.ArgumentNotNull(target, "target"); + + // set serialization options onto reader + CultureInfo previousCulture; + DateTimeZoneHandling? previousDateTimeZoneHandling; + DateParseHandling? previousDateParseHandling; + FloatParseHandling? previousFloatParseHandling; + int? previousMaxDepth; + string previousDateFormatString; + SetupReader(reader, out previousCulture, out previousDateTimeZoneHandling, out previousDateParseHandling, out previousFloatParseHandling, out previousMaxDepth, out previousDateFormatString); + + TraceJsonReader traceJsonReader = (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + ? new TraceJsonReader(reader) + : null; + + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); + serializerReader.Populate(traceJsonReader ?? reader, target); + + if (traceJsonReader != null) + TraceWriter.Trace(TraceLevel.Verbose, "Deserialized JSON: " + Environment.NewLine + traceJsonReader.GetJson(), null); + + ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString); + } + + /// + /// Deserializes the Json structure contained by the specified . + /// + /// The that contains the JSON structure to deserialize. + /// The being deserialized. + public object Deserialize(JsonReader reader) + { + return Deserialize(reader, null); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The of object being deserialized. + /// The instance of being deserialized. + public object Deserialize(TextReader reader, Type objectType) + { + return Deserialize(new JsonTextReader(reader), objectType); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The type of the object to deserialize. + /// The instance of being deserialized. + public T Deserialize(JsonReader reader) + { + return (T)Deserialize(reader, typeof(T)); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The of object being deserialized. + /// The instance of being deserialized. + public object Deserialize(JsonReader reader, Type objectType) + { + return DeserializeInternal(reader, objectType); + } + + internal virtual object DeserializeInternal(JsonReader reader, Type objectType) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + // set serialization options onto reader + CultureInfo previousCulture; + DateTimeZoneHandling? previousDateTimeZoneHandling; + DateParseHandling? previousDateParseHandling; + FloatParseHandling? previousFloatParseHandling; + int? previousMaxDepth; + string previousDateFormatString; + SetupReader(reader, out previousCulture, out previousDateTimeZoneHandling, out previousDateParseHandling, out previousFloatParseHandling, out previousMaxDepth, out previousDateFormatString); + + TraceJsonReader traceJsonReader = (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + ? new TraceJsonReader(reader) + : null; + + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); + object value = serializerReader.Deserialize(traceJsonReader ?? reader, objectType, CheckAdditionalContent); + + if (traceJsonReader != null) + TraceWriter.Trace(TraceLevel.Verbose, "Deserialized JSON: " + Environment.NewLine + traceJsonReader.GetJson(), null); + + ResetReader(reader, previousCulture, previousDateTimeZoneHandling, previousDateParseHandling, previousFloatParseHandling, previousMaxDepth, previousDateFormatString); + + return value; + } + + private void SetupReader(JsonReader reader, out CultureInfo previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string previousDateFormatString) + { + if (_culture != null && !_culture.Equals(reader.Culture)) + { + previousCulture = reader.Culture; + reader.Culture = _culture; + } + else + { + previousCulture = null; + } + + if (_dateTimeZoneHandling != null && reader.DateTimeZoneHandling != _dateTimeZoneHandling) + { + previousDateTimeZoneHandling = reader.DateTimeZoneHandling; + reader.DateTimeZoneHandling = _dateTimeZoneHandling.Value; + } + else + { + previousDateTimeZoneHandling = null; + } + + if (_dateParseHandling != null && reader.DateParseHandling != _dateParseHandling) + { + previousDateParseHandling = reader.DateParseHandling; + reader.DateParseHandling = _dateParseHandling.Value; + } + else + { + previousDateParseHandling = null; + } + + if (_floatParseHandling != null && reader.FloatParseHandling != _floatParseHandling) + { + previousFloatParseHandling = reader.FloatParseHandling; + reader.FloatParseHandling = _floatParseHandling.Value; + } + else + { + previousFloatParseHandling = null; + } + + if (_maxDepthSet && reader.MaxDepth != _maxDepth) + { + previousMaxDepth = reader.MaxDepth; + reader.MaxDepth = _maxDepth; + } + else + { + previousMaxDepth = null; + } + + if (_dateFormatStringSet && reader.DateFormatString != _dateFormatString) + { + previousDateFormatString = reader.DateFormatString; + reader.DateFormatString = _dateFormatString; + } + else + { + previousDateFormatString = null; + } + + JsonTextReader textReader = reader as JsonTextReader; + if (textReader != null) + { + DefaultContractResolver resolver = _contractResolver as DefaultContractResolver; + if (resolver != null) + textReader.NameTable = resolver.GetState().NameTable; + } + } + + private void ResetReader(JsonReader reader, CultureInfo previousCulture, DateTimeZoneHandling? previousDateTimeZoneHandling, DateParseHandling? previousDateParseHandling, FloatParseHandling? previousFloatParseHandling, int? previousMaxDepth, string previousDateFormatString) + { + // reset reader back to previous options + if (previousCulture != null) + reader.Culture = previousCulture; + if (previousDateTimeZoneHandling != null) + reader.DateTimeZoneHandling = previousDateTimeZoneHandling.Value; + if (previousDateParseHandling != null) + reader.DateParseHandling = previousDateParseHandling.Value; + if (previousFloatParseHandling != null) + reader.FloatParseHandling = previousFloatParseHandling.Value; + if (_maxDepthSet) + reader.MaxDepth = previousMaxDepth; + if (_dateFormatStringSet) + reader.DateFormatString = previousDateFormatString; + + JsonTextReader textReader = reader as JsonTextReader; + if (textReader != null) + textReader.NameTable = null; + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + public void Serialize(TextWriter textWriter, object value) + { + Serialize(new JsonTextWriter(textWriter), value); + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + /// + /// The type of the value being serialized. + /// This parameter is used when is Auto to write out the type name if the type of the value does not match. + /// Specifing the type is optional. + /// + public void Serialize(JsonWriter jsonWriter, object value, Type objectType) + { + SerializeInternal(jsonWriter, value, objectType); + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + /// + /// The type of the value being serialized. + /// This parameter is used when is Auto to write out the type name if the type of the value does not match. + /// Specifing the type is optional. + /// + public void Serialize(TextWriter textWriter, object value, Type objectType) + { + Serialize(new JsonTextWriter(textWriter), value, objectType); + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + public void Serialize(JsonWriter jsonWriter, object value) + { + SerializeInternal(jsonWriter, value, null); + } + + internal virtual void SerializeInternal(JsonWriter jsonWriter, object value, Type objectType) + { + ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter"); + + // set serialization options onto writer + Formatting? previousFormatting = null; + if (_formatting != null && jsonWriter.Formatting != _formatting) + { + previousFormatting = jsonWriter.Formatting; + jsonWriter.Formatting = _formatting.Value; + } + + DateFormatHandling? previousDateFormatHandling = null; + if (_dateFormatHandling != null && jsonWriter.DateFormatHandling != _dateFormatHandling) + { + previousDateFormatHandling = jsonWriter.DateFormatHandling; + jsonWriter.DateFormatHandling = _dateFormatHandling.Value; + } + + DateTimeZoneHandling? previousDateTimeZoneHandling = null; + if (_dateTimeZoneHandling != null && jsonWriter.DateTimeZoneHandling != _dateTimeZoneHandling) + { + previousDateTimeZoneHandling = jsonWriter.DateTimeZoneHandling; + jsonWriter.DateTimeZoneHandling = _dateTimeZoneHandling.Value; + } + + FloatFormatHandling? previousFloatFormatHandling = null; + if (_floatFormatHandling != null && jsonWriter.FloatFormatHandling != _floatFormatHandling) + { + previousFloatFormatHandling = jsonWriter.FloatFormatHandling; + jsonWriter.FloatFormatHandling = _floatFormatHandling.Value; + } + + StringEscapeHandling? previousStringEscapeHandling = null; + if (_stringEscapeHandling != null && jsonWriter.StringEscapeHandling != _stringEscapeHandling) + { + previousStringEscapeHandling = jsonWriter.StringEscapeHandling; + jsonWriter.StringEscapeHandling = _stringEscapeHandling.Value; + } + + CultureInfo previousCulture = null; + if (_culture != null && !_culture.Equals(jsonWriter.Culture)) + { + previousCulture = jsonWriter.Culture; + jsonWriter.Culture = _culture; + } + + string previousDateFormatString = null; + if (_dateFormatStringSet && jsonWriter.DateFormatString != _dateFormatString) + { + previousDateFormatString = jsonWriter.DateFormatString; + jsonWriter.DateFormatString = _dateFormatString; + } + + TraceJsonWriter traceJsonWriter = (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + ? new TraceJsonWriter(jsonWriter) + : null; + + JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(this); + serializerWriter.Serialize(traceJsonWriter ?? jsonWriter, value, objectType); + + if (traceJsonWriter != null) + TraceWriter.Trace(TraceLevel.Verbose, "Serialized JSON: " + Environment.NewLine + traceJsonWriter.GetJson(), null); + + // reset writer back to previous options + if (previousFormatting != null) + jsonWriter.Formatting = previousFormatting.Value; + if (previousDateFormatHandling != null) + jsonWriter.DateFormatHandling = previousDateFormatHandling.Value; + if (previousDateTimeZoneHandling != null) + jsonWriter.DateTimeZoneHandling = previousDateTimeZoneHandling.Value; + if (previousFloatFormatHandling != null) + jsonWriter.FloatFormatHandling = previousFloatFormatHandling.Value; + if (previousStringEscapeHandling != null) + jsonWriter.StringEscapeHandling = previousStringEscapeHandling.Value; + if (_dateFormatStringSet) + jsonWriter.DateFormatString = previousDateFormatString; + if (previousCulture != null) + jsonWriter.Culture = previousCulture; + } + + internal IReferenceResolver GetReferenceResolver() + { + if (_referenceResolver == null) + _referenceResolver = new DefaultReferenceResolver(); + + return _referenceResolver; + } + + internal JsonConverter GetMatchingConverter(Type type) + { + return GetMatchingConverter(_converters, type); + } + + internal static JsonConverter GetMatchingConverter(IList converters, Type objectType) + { +#if DEBUG + ValidationUtils.ArgumentNotNull(objectType, "objectType"); +#endif + + if (converters != null) + { + for (int i = 0; i < converters.Count; i++) + { + JsonConverter converter = converters[i]; + + if (converter.CanConvert(objectType)) + return converter; + } + } + + return null; + } + + internal void OnError(ErrorEventArgs e) + { + EventHandler error = Error; + if (error != null) + error(this, e); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonSerializerSettings.cs b/Libs/JsonNet/JsonSerializerSettings.cs new file mode 100644 index 0000000..96949e6 --- /dev/null +++ b/Libs/JsonNet/JsonSerializerSettings.cs @@ -0,0 +1,365 @@ +#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.Globalization; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Serialization; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the settings on a object. + /// + public class JsonSerializerSettings + { + internal const ReferenceLoopHandling DefaultReferenceLoopHandling = ReferenceLoopHandling.Error; + internal const MissingMemberHandling DefaultMissingMemberHandling = MissingMemberHandling.Ignore; + internal const NullValueHandling DefaultNullValueHandling = NullValueHandling.Include; + internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include; + internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto; + internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None; + internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default; + internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None; + internal const MetadataPropertyHandling DefaultMetadataPropertyHandling = MetadataPropertyHandling.Default; + internal const FormatterAssemblyStyle DefaultTypeNameAssemblyFormat = FormatterAssemblyStyle.Simple; + internal static readonly StreamingContext DefaultContext; + + internal const Formatting DefaultFormatting = Formatting.None; + internal const DateFormatHandling DefaultDateFormatHandling = DateFormatHandling.IsoDateFormat; + internal const DateTimeZoneHandling DefaultDateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + internal const DateParseHandling DefaultDateParseHandling = DateParseHandling.DateTime; + internal const FloatParseHandling DefaultFloatParseHandling = FloatParseHandling.Double; + internal const FloatFormatHandling DefaultFloatFormatHandling = FloatFormatHandling.String; + internal const StringEscapeHandling DefaultStringEscapeHandling = StringEscapeHandling.Default; + internal const FormatterAssemblyStyle DefaultFormatterAssemblyStyle = FormatterAssemblyStyle.Simple; + internal static readonly CultureInfo DefaultCulture; + internal const bool DefaultCheckAdditionalContent = false; + internal const string DefaultDateFormatString = @"yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; + + internal Formatting? _formatting; + internal DateFormatHandling? _dateFormatHandling; + internal DateTimeZoneHandling? _dateTimeZoneHandling; + internal DateParseHandling? _dateParseHandling; + internal FloatFormatHandling? _floatFormatHandling; + internal FloatParseHandling? _floatParseHandling; + internal StringEscapeHandling? _stringEscapeHandling; + internal CultureInfo _culture; + internal bool? _checkAdditionalContent; + internal int? _maxDepth; + internal bool _maxDepthSet; + internal string _dateFormatString; + internal bool _dateFormatStringSet; + internal FormatterAssemblyStyle? _typeNameAssemblyFormat; + internal DefaultValueHandling? _defaultValueHandling; + internal PreserveReferencesHandling? _preserveReferencesHandling; + internal NullValueHandling? _nullValueHandling; + internal ObjectCreationHandling? _objectCreationHandling; + internal MissingMemberHandling? _missingMemberHandling; + internal ReferenceLoopHandling? _referenceLoopHandling; + internal StreamingContext? _context; + internal ConstructorHandling? _constructorHandling; + internal TypeNameHandling? _typeNameHandling; + internal MetadataPropertyHandling? _metadataPropertyHandling; + + /// + /// Gets or sets how reference loops (e.g. a class referencing itself) is handled. + /// + /// Reference loop handling. + public ReferenceLoopHandling ReferenceLoopHandling + { + get { return _referenceLoopHandling ?? DefaultReferenceLoopHandling; } + set { _referenceLoopHandling = value; } + } + + /// + /// Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + /// + /// Missing member handling. + public MissingMemberHandling MissingMemberHandling + { + get { return _missingMemberHandling ?? DefaultMissingMemberHandling; } + set { _missingMemberHandling = value; } + } + + /// + /// Gets or sets how objects are created during deserialization. + /// + /// The object creation handling. + public ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling ?? DefaultObjectCreationHandling; } + set { _objectCreationHandling = value; } + } + + /// + /// Gets or sets how null values are handled during serialization and deserialization. + /// + /// Null value handling. + public NullValueHandling NullValueHandling + { + get { return _nullValueHandling ?? DefaultNullValueHandling; } + set { _nullValueHandling = value; } + } + + /// + /// Gets or sets how null default are handled during serialization and deserialization. + /// + /// The default value handling. + public DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling ?? DefaultDefaultValueHandling; } + set { _defaultValueHandling = value; } + } + + /// + /// Gets or sets a collection that will be used during serialization. + /// + /// The converters. + public IList Converters { get; set; } + + /// + /// Gets or sets how object references are preserved by the serializer. + /// + /// The preserve references handling. + public PreserveReferencesHandling PreserveReferencesHandling + { + get { return _preserveReferencesHandling ?? DefaultPreserveReferencesHandling; } + set { _preserveReferencesHandling = value; } + } + + /// + /// Gets or sets how type name writing and reading is handled by the serializer. + /// + /// The type name handling. + public TypeNameHandling TypeNameHandling + { + get { return _typeNameHandling ?? DefaultTypeNameHandling; } + set { _typeNameHandling = value; } + } + + /// + /// Gets or sets how metadata properties are used during deserialization. + /// + /// The metadata properties handling. + public MetadataPropertyHandling MetadataPropertyHandling + { + get { return _metadataPropertyHandling ?? DefaultMetadataPropertyHandling; } + set { _metadataPropertyHandling = value; } + } + + /// + /// Gets or sets how a type name assembly is written and resolved by the serializer. + /// + /// The type name assembly format. + public FormatterAssemblyStyle TypeNameAssemblyFormat + { + get { return _typeNameAssemblyFormat ?? DefaultFormatterAssemblyStyle; } + set { _typeNameAssemblyFormat = value; } + } + + /// + /// Gets or sets how constructors are used during deserialization. + /// + /// The constructor handling. + public ConstructorHandling ConstructorHandling + { + get { return _constructorHandling ?? DefaultConstructorHandling; } + set { _constructorHandling = value; } + } + + /// + /// Gets or sets the contract resolver used by the serializer when + /// serializing .NET objects to JSON and vice versa. + /// + /// The contract resolver. + public IContractResolver ContractResolver { get; set; } + + /// + /// Gets or sets the used by the serializer when resolving references. + /// + /// The reference resolver. + public IReferenceResolver ReferenceResolver { get; set; } + + /// + /// Gets or sets the used by the serializer when writing trace messages. + /// + /// The trace writer. + public ITraceWriter TraceWriter { get; set; } + + /// + /// Gets or sets the used by the serializer when resolving type names. + /// + /// The binder. + public SerializationBinder Binder { get; set; } + + /// + /// Gets or sets the error handler called during serialization and deserialization. + /// + /// The error handler called during serialization and deserialization. + public EventHandler Error { get; set; } + + /// + /// Gets or sets the used by the serializer when invoking serialization callback methods. + /// + /// The context. + public StreamingContext Context + { + get { return _context ?? DefaultContext; } + set { _context = value; } + } + + /// + /// Get or set how and values are formatting when writing JSON text. + /// + public string DateFormatString + { + get { return _dateFormatString ?? DefaultDateFormatString; } + set + { + _dateFormatString = value; + _dateFormatStringSet = true; + } + } + + /// + /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . + /// + public int? MaxDepth + { + get { return _maxDepth; } + set + { + if (value <= 0) + throw new ArgumentException("Value must be positive.", "value"); + + _maxDepth = value; + _maxDepthSet = true; + } + } + + /// + /// Indicates how JSON text output is formatted. + /// + public Formatting Formatting + { + get { return _formatting ?? DefaultFormatting; } + set { _formatting = value; } + } + + /// + /// Get or set how dates are written to JSON text. + /// + public DateFormatHandling DateFormatHandling + { + get { return _dateFormatHandling ?? DefaultDateFormatHandling; } + set { _dateFormatHandling = value; } + } + + /// + /// Get or set how time zones are handling during serialization and deserialization. + /// + public DateTimeZoneHandling DateTimeZoneHandling + { + get { return _dateTimeZoneHandling ?? DefaultDateTimeZoneHandling; } + set { _dateTimeZoneHandling = value; } + } + + /// + /// Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. + /// + public DateParseHandling DateParseHandling + { + get { return _dateParseHandling ?? DefaultDateParseHandling; } + set { _dateParseHandling = value; } + } + + /// + /// Get or set how special floating point numbers, e.g. , + /// and , + /// are written as JSON. + /// + public FloatFormatHandling FloatFormatHandling + { + get { return _floatFormatHandling ?? DefaultFloatFormatHandling; } + set { _floatFormatHandling = value; } + } + + /// + /// Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. + /// + public FloatParseHandling FloatParseHandling + { + get { return _floatParseHandling ?? DefaultFloatParseHandling; } + set { _floatParseHandling = value; } + } + + /// + /// Get or set how strings are escaped when writing JSON text. + /// + public StringEscapeHandling StringEscapeHandling + { + get { return _stringEscapeHandling ?? DefaultStringEscapeHandling; } + set { _stringEscapeHandling = value; } + } + + /// + /// Gets or sets the culture used when reading JSON. Defaults to . + /// + public CultureInfo Culture + { + get { return _culture ?? DefaultCulture; } + set { _culture = value; } + } + + /// + /// Gets a value indicating whether there will be a check for additional content after deserializing an object. + /// + /// + /// true if there will be a check for additional content after deserializing an object; otherwise, false. + /// + public bool CheckAdditionalContent + { + get { return _checkAdditionalContent ?? DefaultCheckAdditionalContent; } + set { _checkAdditionalContent = value; } + } + + static JsonSerializerSettings() + { + DefaultContext = new StreamingContext(); + DefaultCulture = CultureInfo.InvariantCulture; + } + + /// + /// Initializes a new instance of the class. + /// + public JsonSerializerSettings() + { + Converters = new List(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonTextReader.cs b/Libs/JsonNet/JsonTextReader.cs new file mode 100644 index 0000000..18e9d57 --- /dev/null +++ b/Libs/JsonNet/JsonTextReader.cs @@ -0,0 +1,1671 @@ +#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.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using System.IO; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + internal enum ReadType + { + Read, + ReadAsInt32, + ReadAsBytes, + ReadAsString, + ReadAsDecimal, + ReadAsDateTime, +#if !NET20 + ReadAsDateTimeOffset +#endif + } + + /// + /// Represents a reader that provides fast, non-cached, forward-only access to JSON text data. + /// + public class JsonTextReader : JsonReader, IJsonLineInfo + { + private const char UnicodeReplacementChar = '\uFFFD'; + private const int MaximumJavascriptIntegerCharacterLength = 380; + + private readonly TextReader _reader; + private char[] _chars; + private int _charsUsed; + private int _charPos; + private int _lineStartPos; + private int _lineNumber; + private bool _isEndOfFile; + private StringBuffer _buffer; + private StringReference _stringReference; + internal PropertyNameTable NameTable; + + /// + /// 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; + _lineNumber = 1; + _chars = new char[1025]; + } + +#if DEBUG + internal void SetCharBuffer(char[] chars) + { + _chars = chars; + } +#endif + + private StringBuffer GetBuffer() + { + if (_buffer == null) + { + _buffer = new StringBuffer(1025); + } + else + { + _buffer.Position = 0; + } + + return _buffer; + } + + private void OnNewLine(int pos) + { + _lineNumber++; + _lineStartPos = pos - 1; + } + + private void ParseString(char quote) + { + _charPos++; + + ShiftBufferIfNeeded(); + ReadStringIntoBuffer(quote); + SetPostValueState(true); + + if (_readType == ReadType.ReadAsBytes) + { + byte[] data; + if (_stringReference.Length == 0) + { + data = new byte[0]; + } + else + { + data = Convert.FromBase64CharArray(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length); + } + + SetToken(JsonToken.Bytes, data, false); + } + else if (_readType == ReadType.ReadAsString) + { + string text = _stringReference.ToString(); + + SetToken(JsonToken.String, text, false); + _quoteChar = quote; + } + else + { + string text = _stringReference.ToString(); + + if (_dateParseHandling != DateParseHandling.None) + { + DateParseHandling dateParseHandling; + if (_readType == ReadType.ReadAsDateTime) + dateParseHandling = DateParseHandling.DateTime; +#if !NET20 + else if (_readType == ReadType.ReadAsDateTimeOffset) + dateParseHandling = DateParseHandling.DateTimeOffset; +#endif + else + dateParseHandling = _dateParseHandling; + + object dt; + if (DateTimeUtils.TryParseDateTime(text, dateParseHandling, DateTimeZoneHandling, DateFormatString, Culture, out dt)) + { + SetToken(JsonToken.Date, dt, false); + return; + } + } + + SetToken(JsonToken.String, text, false); + _quoteChar = quote; + } + } + + private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count) + { + const int charByteCount = 2; + + Buffer.BlockCopy(src, srcOffset * charByteCount, dst, dstOffset * charByteCount, count * charByteCount); + } + + private void ShiftBufferIfNeeded() + { + // once in the last 10% of the buffer shift the remaining content to the start to avoid + // unnessesarly increasing the buffer size when reading numbers/strings + int length = _chars.Length; + if (length - _charPos <= length * 0.1) + { + int count = _charsUsed - _charPos; + if (count > 0) + BlockCopyChars(_chars, _charPos, _chars, 0, count); + + _lineStartPos -= _charPos; + _charPos = 0; + _charsUsed = count; + _chars[_charsUsed] = '\0'; + } + } + + private int ReadData(bool append) + { + return ReadData(append, 0); + } + + private int ReadData(bool append, int charsRequired) + { + if (_isEndOfFile) + return 0; + + // char buffer is full + if (_charsUsed + charsRequired >= _chars.Length - 1) + { + if (append) + { + // copy to new array either double the size of the current or big enough to fit required content + int newArrayLength = Math.Max(_chars.Length * 2, _charsUsed + charsRequired + 1); + + // increase the size of the buffer + char[] dst = new char[newArrayLength]; + + BlockCopyChars(_chars, 0, dst, 0, _chars.Length); + + _chars = dst; + } + else + { + int remainingCharCount = _charsUsed - _charPos; + + if (remainingCharCount + charsRequired + 1 >= _chars.Length) + { + // the remaining count plus the required is bigger than the current buffer size + char[] dst = new char[remainingCharCount + charsRequired + 1]; + + if (remainingCharCount > 0) + BlockCopyChars(_chars, _charPos, dst, 0, remainingCharCount); + + _chars = dst; + } + else + { + // copy any remaining data to the beginning of the buffer if needed and reset positions + if (remainingCharCount > 0) + BlockCopyChars(_chars, _charPos, _chars, 0, remainingCharCount); + } + + _lineStartPos -= _charPos; + _charPos = 0; + _charsUsed = remainingCharCount; + } + } + + int attemptCharReadCount = _chars.Length - _charsUsed - 1; + + int charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount); + + _charsUsed += charsRead; + + if (charsRead == 0) + _isEndOfFile = true; + + _chars[_charsUsed] = '\0'; + return charsRead; + } + + private bool EnsureChars(int relativePosition, bool append) + { + if (_charPos + relativePosition >= _charsUsed) + return ReadChars(relativePosition, append); + + return true; + } + + private bool ReadChars(int relativePosition, bool append) + { + if (_isEndOfFile) + return false; + + int charsRequired = _charPos + relativePosition - _charsUsed + 1; + + int totalCharsRead = 0; + + // it is possible that the TextReader doesn't return all data at once + // repeat read until the required text is returned or the reader is out of content + do + { + int charsRead = ReadData(append, charsRequired - totalCharsRead); + + // no more content + if (charsRead == 0) + break; + + totalCharsRead += charsRead; + } while (totalCharsRead < charsRequired); + + if (totalCharsRead < charsRequired) + return false; + return true; + } + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + [DebuggerStepThrough] + public override bool Read() + { + _readType = ReadType.Read; + if (!ReadInternal()) + { + SetToken(JsonToken.None); + return false; + } + + return true; + } + + /// + /// Reads the next JSON token from the stream as a []. + /// + /// + /// A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. + /// + public override byte[] ReadAsBytes() + { + return ReadAsBytesInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override decimal? ReadAsDecimal() + { + return ReadAsDecimalInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override int? ReadAsInt32() + { + return ReadAsInt32Internal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override string ReadAsString() + { + return ReadAsStringInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTime? ReadAsDateTime() + { + return ReadAsDateTimeInternal(); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTimeOffset? ReadAsDateTimeOffset() + { + return ReadAsDateTimeOffsetInternal(); + } +#endif + + internal override bool ReadInternal() + { + while (true) + { + switch (_currentState) + { + case State.Start: + case State.Property: + case State.Array: + case State.ArrayStart: + case State.Constructor: + case State.ConstructorStart: + return ParseValue(); + 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.Finished: + if (EnsureChars(0, false)) + { + EatWhitespace(false); + if (_isEndOfFile) + { + return false; + } + if (_chars[_charPos] == '/') + { + ParseComment(); + return true; + } + + throw JsonReaderException.Create(this, "Additional text encountered after finished reading JSON content: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + } + return false; + default: + throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState)); + } + } + } + + private void ReadStringIntoBuffer(char quote) + { + int charPos = _charPos; + int initialPosition = _charPos; + int lastWritePosition = _charPos; + StringBuffer buffer = null; + + while (true) + { + switch (_chars[charPos++]) + { + case '\0': + if (_charsUsed == charPos - 1) + { + charPos--; + + if (ReadData(true) == 0) + { + _charPos = charPos; + throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote)); + } + } + break; + case '\\': + _charPos = charPos; + if (!EnsureChars(0, true)) + { + _charPos = charPos; + throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote)); + } + + // start of escape sequence + int escapeStartPos = charPos - 1; + + char currentChar = _chars[charPos]; + + char writeChar; + + switch (currentChar) + { + case 'b': + charPos++; + writeChar = '\b'; + break; + case 't': + charPos++; + writeChar = '\t'; + break; + case 'n': + charPos++; + writeChar = '\n'; + break; + case 'f': + charPos++; + writeChar = '\f'; + break; + case 'r': + charPos++; + writeChar = '\r'; + break; + case '\\': + charPos++; + writeChar = '\\'; + break; + case '"': + case '\'': + case '/': + writeChar = currentChar; + charPos++; + break; + case 'u': + charPos++; + _charPos = charPos; + writeChar = ParseUnicode(); + + if (StringUtils.IsLowSurrogate(writeChar)) + { + // low surrogate with no preceding high surrogate; this char is replaced + writeChar = UnicodeReplacementChar; + } + else if (StringUtils.IsHighSurrogate(writeChar)) + { + bool anotherHighSurrogate; + + // loop for handling situations where there are multiple consecutive high surrogates + do + { + anotherHighSurrogate = false; + + // potential start of a surrogate pair + if (EnsureChars(2, true) && _chars[_charPos] == '\\' && _chars[_charPos + 1] == 'u') + { + char highSurrogate = writeChar; + + _charPos += 2; + writeChar = ParseUnicode(); + + if (StringUtils.IsLowSurrogate(writeChar)) + { + // a valid surrogate pair! + } + else if (StringUtils.IsHighSurrogate(writeChar)) + { + // another high surrogate; replace current and start check over + highSurrogate = UnicodeReplacementChar; + anotherHighSurrogate = true; + } + else + { + // high surrogate not followed by low surrogate; original char is replaced + highSurrogate = UnicodeReplacementChar; + } + + if (buffer == null) + buffer = GetBuffer(); + + WriteCharToBuffer(buffer, highSurrogate, lastWritePosition, escapeStartPos); + lastWritePosition = _charPos; + } + else + { + // there are not enough remaining chars for the low surrogate or is not follow by unicode sequence + // replace high surrogate and continue on as usual + writeChar = UnicodeReplacementChar; + } + } while (anotherHighSurrogate); + } + + charPos = _charPos; + break; + default: + charPos++; + _charPos = charPos; + throw JsonReaderException.Create(this, "Bad JSON escape sequence: {0}.".FormatWith(CultureInfo.InvariantCulture, @"\" + currentChar)); + } + + if (buffer == null) + buffer = GetBuffer(); + + WriteCharToBuffer(buffer, writeChar, lastWritePosition, escapeStartPos); + + lastWritePosition = charPos; + break; + case StringUtils.CarriageReturn: + _charPos = charPos - 1; + ProcessCarriageReturn(true); + charPos = _charPos; + break; + case StringUtils.LineFeed: + _charPos = charPos - 1; + ProcessLineFeed(); + charPos = _charPos; + break; + case '"': + case '\'': + if (_chars[charPos - 1] == quote) + { + charPos--; + + if (initialPosition == lastWritePosition) + { + _stringReference = new StringReference(_chars, initialPosition, charPos - initialPosition); + } + else + { + if (buffer == null) + buffer = GetBuffer(); + + if (charPos > lastWritePosition) + buffer.Append(_chars, lastWritePosition, charPos - lastWritePosition); + + _stringReference = new StringReference(buffer.GetInternalBuffer(), 0, buffer.Position); + } + + charPos++; + _charPos = charPos; + return; + } + break; + } + } + } + + private void WriteCharToBuffer(StringBuffer buffer, char writeChar, int lastWritePosition, int writeToPosition) + { + if (writeToPosition > lastWritePosition) + { + buffer.Append(_chars, lastWritePosition, writeToPosition - lastWritePosition); + } + + buffer.Append(writeChar); + } + + private char ParseUnicode() + { + char writeChar; + if (EnsureChars(4, true)) + { + string hexValues = new string(_chars, _charPos, 4); + char hexChar = Convert.ToChar(int.Parse(hexValues, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); + writeChar = hexChar; + + _charPos += 4; + } + else + { + throw JsonReaderException.Create(this, "Unexpected end while parsing unicode character."); + } + return writeChar; + } + + private void ReadNumberIntoBuffer() + { + int charPos = _charPos; + + while (true) + { + switch (_chars[charPos]) + { + case '\0': + _charPos = charPos; + + if (_charsUsed == charPos) + { + if (ReadData(true) == 0) + return; + } + else + { + return; + } + break; + case '-': + case '+': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + case 'x': + case 'X': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + charPos++; + break; + default: + _charPos = charPos; + + char currentChar = _chars[_charPos]; + if (char.IsWhiteSpace(currentChar) + || currentChar == ',' + || currentChar == '}' + || currentChar == ']' + || currentChar == ')' + || currentChar == '/') + { + return; + } + + throw JsonReaderException.Create(this, "Unexpected character encountered while parsing number: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar)); + } + } + } + + private void ClearRecentString() + { + if (_buffer != null) + _buffer.Position = 0; + + _stringReference = new StringReference(); + } + + private bool ParsePostValue() + { + while (true) + { + char currentChar = _chars[_charPos]; + + switch (currentChar) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(false) == 0) + { + _currentState = State.Finished; + return false; + } + } + else + { + _charPos++; + } + break; + case '}': + _charPos++; + SetToken(JsonToken.EndObject); + return true; + case ']': + _charPos++; + SetToken(JsonToken.EndArray); + return true; + case ')': + _charPos++; + SetToken(JsonToken.EndConstructor); + return true; + case '/': + ParseComment(); + return true; + case ',': + _charPos++; + + // finished parsing + SetStateBasedOnCurrent(); + return false; + case ' ': + case StringUtils.Tab: + // eat + _charPos++; + break; + case StringUtils.CarriageReturn: + ProcessCarriageReturn(false); + break; + case StringUtils.LineFeed: + ProcessLineFeed(); + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + _charPos++; + } + else + { + throw JsonReaderException.Create(this, "After parsing a value an unexpected character was encountered: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar)); + } + break; + } + } + } + + private bool ParseObject() + { + while (true) + { + char currentChar = _chars[_charPos]; + + switch (currentChar) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(false) == 0) + return false; + } + else + { + _charPos++; + } + break; + case '}': + SetToken(JsonToken.EndObject); + _charPos++; + return true; + case '/': + ParseComment(); + return true; + case StringUtils.CarriageReturn: + ProcessCarriageReturn(false); + break; + case StringUtils.LineFeed: + ProcessLineFeed(); + break; + case ' ': + case StringUtils.Tab: + // eat + _charPos++; + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + _charPos++; + } + else + { + return ParseProperty(); + } + break; + } + } + } + + private bool ParseProperty() + { + char firstChar = _chars[_charPos]; + char quoteChar; + + if (firstChar == '"' || firstChar == '\'') + { + _charPos++; + quoteChar = firstChar; + ShiftBufferIfNeeded(); + ReadStringIntoBuffer(quoteChar); + } + else if (ValidIdentifierChar(firstChar)) + { + quoteChar = '\0'; + ShiftBufferIfNeeded(); + ParseUnquotedProperty(); + } + else + { + throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + } + + string propertyName; + + if (NameTable != null) + { + propertyName = NameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length); + + // no match in name table + if (propertyName == null) + propertyName = _stringReference.ToString(); + } + else + { + propertyName = _stringReference.ToString(); + } + + EatWhitespace(false); + + if (_chars[_charPos] != ':') + throw JsonReaderException.Create(this, "Invalid character after parsing property name. Expected ':' but got: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + + _charPos++; + + SetToken(JsonToken.PropertyName, propertyName); + _quoteChar = quoteChar; + ClearRecentString(); + + return true; + } + + private bool ValidIdentifierChar(char value) + { + return (char.IsLetterOrDigit(value) || value == '_' || value == '$'); + } + + private void ParseUnquotedProperty() + { + int initialPosition = _charPos; + + // parse unquoted property name until whitespace or colon + while (true) + { + switch (_chars[_charPos]) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(true) == 0) + throw JsonReaderException.Create(this, "Unexpected end while parsing unquoted property name."); + + break; + } + + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + return; + default: + char currentChar = _chars[_charPos]; + + if (ValidIdentifierChar(currentChar)) + { + _charPos++; + break; + } + else if (char.IsWhiteSpace(currentChar) || currentChar == ':') + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + return; + } + + throw JsonReaderException.Create(this, "Invalid JavaScript property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar)); + } + } + } + + private bool ParseValue() + { + while (true) + { + char currentChar = _chars[_charPos]; + + switch (currentChar) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(false) == 0) + return false; + } + else + { + _charPos++; + } + break; + case '"': + case '\'': + ParseString(currentChar); + return true; + case 't': + ParseTrue(); + return true; + case 'f': + ParseFalse(); + return true; + case 'n': + if (EnsureChars(1, true)) + { + char next = _chars[_charPos + 1]; + + if (next == 'u') + ParseNull(); + else if (next == 'e') + ParseConstructor(); + else + throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + } + else + { + throw JsonReaderException.Create(this, "Unexpected end."); + } + return true; + case 'N': + ParseNumberNaN(); + return true; + case 'I': + ParseNumberPositiveInfinity(); + return true; + case '-': + if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I') + ParseNumberNegativeInfinity(); + else + ParseNumber(); + return true; + case '/': + ParseComment(); + return true; + case 'u': + ParseUndefined(); + return true; + case '{': + _charPos++; + SetToken(JsonToken.StartObject); + return true; + case '[': + _charPos++; + SetToken(JsonToken.StartArray); + return true; + case ']': + _charPos++; + SetToken(JsonToken.EndArray); + return true; + case ',': + // don't increment position, the next call to read will handle comma + // this is done to handle multiple empty comma values + SetToken(JsonToken.Undefined); + return true; + case ')': + _charPos++; + SetToken(JsonToken.EndConstructor); + return true; + case StringUtils.CarriageReturn: + ProcessCarriageReturn(false); + break; + case StringUtils.LineFeed: + ProcessLineFeed(); + break; + case ' ': + case StringUtils.Tab: + // eat + _charPos++; + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + _charPos++; + break; + } + if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.') + { + ParseNumber(); + return true; + } + + throw JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar)); + } + } + } + + private void ProcessLineFeed() + { + _charPos++; + OnNewLine(_charPos); + } + + private void ProcessCarriageReturn(bool append) + { + _charPos++; + + if (EnsureChars(1, append) && _chars[_charPos] == StringUtils.LineFeed) + _charPos++; + + OnNewLine(_charPos); + } + + private bool EatWhitespace(bool oneOrMore) + { + bool finished = false; + bool ateWhitespace = false; + while (!finished) + { + char currentChar = _chars[_charPos]; + + switch (currentChar) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(false) == 0) + finished = true; + } + else + { + _charPos++; + } + break; + case StringUtils.CarriageReturn: + ProcessCarriageReturn(false); + break; + case StringUtils.LineFeed: + ProcessLineFeed(); + break; + default: + if (currentChar == ' ' || char.IsWhiteSpace(currentChar)) + { + ateWhitespace = true; + _charPos++; + } + else + { + finished = true; + } + break; + } + } + + return (!oneOrMore || ateWhitespace); + } + + private void ParseConstructor() + { + if (MatchValueWithTrailingSeparator("new")) + { + EatWhitespace(false); + + int initialPosition = _charPos; + int endPosition; + + while (true) + { + char currentChar = _chars[_charPos]; + if (currentChar == '\0') + { + if (_charsUsed == _charPos) + { + if (ReadData(true) == 0) + throw JsonReaderException.Create(this, "Unexpected end while parsing constructor."); + } + else + { + endPosition = _charPos; + _charPos++; + break; + } + } + else if (char.IsLetterOrDigit(currentChar)) + { + _charPos++; + } + else if (currentChar == StringUtils.CarriageReturn) + { + endPosition = _charPos; + ProcessCarriageReturn(true); + break; + } + else if (currentChar == StringUtils.LineFeed) + { + endPosition = _charPos; + ProcessLineFeed(); + break; + } + else if (char.IsWhiteSpace(currentChar)) + { + endPosition = _charPos; + _charPos++; + break; + } + else if (currentChar == '(') + { + endPosition = _charPos; + break; + } + else + { + throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar)); + } + } + + _stringReference = new StringReference(_chars, initialPosition, endPosition - initialPosition); + string constructorName = _stringReference.ToString(); + + EatWhitespace(false); + + if (_chars[_charPos] != '(') + throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + + _charPos++; + + ClearRecentString(); + + SetToken(JsonToken.StartConstructor, constructorName); + } + else + { + throw JsonReaderException.Create(this, "Unexpected content while parsing JSON."); + } + } + + private void ParseNumber() + { + ShiftBufferIfNeeded(); + + char firstChar = _chars[_charPos]; + int initialPosition = _charPos; + + ReadNumberIntoBuffer(); + + // set state to PostValue now so that if there is an error parsing the number then the reader can continue + SetPostValueState(true); + + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + + object numberValue; + JsonToken numberType; + + bool singleDigit = (char.IsDigit(firstChar) && _stringReference.Length == 1); + bool nonBase10 = (firstChar == '0' && _stringReference.Length > 1 + && _stringReference.Chars[_stringReference.StartIndex + 1] != '.' + && _stringReference.Chars[_stringReference.StartIndex + 1] != 'e' + && _stringReference.Chars[_stringReference.StartIndex + 1] != 'E'); + + if (_readType == ReadType.ReadAsInt32) + { + if (singleDigit) + { + // digit char values start at 48 + numberValue = firstChar - 48; + } + else if (nonBase10) + { + string number = _stringReference.ToString(); + + try + { + int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt32(number, 16) + : Convert.ToInt32(number, 8); + + numberValue = integer; + } + catch (Exception ex) + { + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex); + } + } + else + { + int value; + ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value); + if (parseResult == ParseResult.Success) + numberValue = value; + else if (parseResult == ParseResult.Overflow) + throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + } + + numberType = JsonToken.Integer; + } + else if (_readType == ReadType.ReadAsDecimal) + { + if (singleDigit) + { + // digit char values start at 48 + numberValue = (decimal)firstChar - 48; + } + else if (nonBase10) + { + string number = _stringReference.ToString(); + + try + { + // decimal.Parse doesn't support parsing hexadecimal values + long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt64(number, 16) + : Convert.ToInt64(number, 8); + + numberValue = Convert.ToDecimal(integer); + } + catch (Exception ex) + { + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex); + } + } + else + { + string number = _stringReference.ToString(); + + decimal value; + if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value)) + numberValue = value; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + } + + numberType = JsonToken.Float; + } + else + { + if (singleDigit) + { + // digit char values start at 48 + numberValue = (long)firstChar - 48; + numberType = JsonToken.Integer; + } + else if (nonBase10) + { + string number = _stringReference.ToString(); + + try + { + numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt64(number, 16) + : Convert.ToInt64(number, 8); + } + catch (Exception ex) + { + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex); + } + + numberType = JsonToken.Integer; + } + else + { + long value; + ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value); + if (parseResult == ParseResult.Success) + { + numberValue = value; + numberType = JsonToken.Integer; + } + else if (parseResult == ParseResult.Overflow) + { +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + string number = _stringReference.ToString(); + + if (number.Length > MaximumJavascriptIntegerCharacterLength) + throw JsonReaderException.Create(this, "JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + + numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture); + numberType = JsonToken.Integer; +#else + throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); +#endif + } + else + { + string number = _stringReference.ToString(); + + if (_floatParseHandling == FloatParseHandling.Decimal) + { + decimal d; + if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d)) + numberValue = d; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number)); + } + else + { + double d; + if (double.TryParse(number, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d)) + numberValue = d; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number)); + } + + numberType = JsonToken.Float; + } + } + } + + ClearRecentString(); + + // index has already been updated + SetToken(numberType, numberValue, false); + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + // By using the BigInteger type in a separate method, + // the runtime can execute the ParseNumber even if + // the System.Numerics.BigInteger.Parse method is + // missing, which happens in some versions of Mono + [MethodImpl(MethodImplOptions.NoInlining)] + private static object BigIntegerParse(string number, CultureInfo culture) + { + return System.Numerics.BigInteger.Parse(number, culture); + } +#endif + + private void ParseComment() + { + // should have already parsed / character before reaching this method + _charPos++; + + if (!EnsureChars(1, false)) + throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); + + bool singlelineComment; + + if (_chars[_charPos] == '*') + singlelineComment = false; + else if (_chars[_charPos] == '/') + singlelineComment = true; + else + throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + + _charPos++; + + int initialPosition = _charPos; + + bool commentFinished = false; + + while (!commentFinished) + { + switch (_chars[_charPos]) + { + case '\0': + if (_charsUsed == _charPos) + { + if (ReadData(true) == 0) + { + if (!singlelineComment) + throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); + + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } + } + else + { + _charPos++; + } + break; + case '*': + _charPos++; + + if (!singlelineComment) + { + if (EnsureChars(0, true)) + { + if (_chars[_charPos] == '/') + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition - 1); + + _charPos++; + commentFinished = true; + } + } + } + break; + case StringUtils.CarriageReturn: + if (singlelineComment) + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } + ProcessCarriageReturn(true); + break; + case StringUtils.LineFeed: + if (singlelineComment) + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } + ProcessLineFeed(); + break; + default: + _charPos++; + break; + } + } + + SetToken(JsonToken.Comment, _stringReference.ToString()); + + ClearRecentString(); + } + + private bool MatchValue(string value) + { + if (!EnsureChars(value.Length - 1, true)) + return false; + + for (int i = 0; i < value.Length; i++) + { + if (_chars[_charPos + i] != value[i]) + { + return false; + } + } + + _charPos += value.Length; + + return true; + } + + private bool MatchValueWithTrailingSeparator(string value) + { + // will match value and then move to the next character, checking that it is a separator character + bool match = MatchValue(value); + + if (!match) + return false; + + if (!EnsureChars(0, false)) + return true; + + return IsSeparator(_chars[_charPos]) || _chars[_charPos] == '\0'; + } + + private bool IsSeparator(char c) + { + switch (c) + { + case '}': + case ']': + case ',': + return true; + case '/': + // check next character to see if start of a comment + if (!EnsureChars(1, false)) + return false; + + var nextChart = _chars[_charPos + 1]; + + return (nextChart == '*' || nextChart == '/'); + case ')': + if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart) + return true; + break; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + return true; + default: + if (char.IsWhiteSpace(c)) + return true; + break; + } + + return false; + } + + private void ParseTrue() + { + // check characters equal 'true' + // and that it is followed by either a separator character + // or the text ends + if (MatchValueWithTrailingSeparator(JsonConvert.True)) + { + SetToken(JsonToken.Boolean, true); + } + else + { + throw JsonReaderException.Create(this, "Error parsing boolean value."); + } + } + + private void ParseNull() + { + if (MatchValueWithTrailingSeparator(JsonConvert.Null)) + { + SetToken(JsonToken.Null); + } + else + { + throw JsonReaderException.Create(this, "Error parsing null value."); + } + } + + private void ParseUndefined() + { + if (MatchValueWithTrailingSeparator(JsonConvert.Undefined)) + { + SetToken(JsonToken.Undefined); + } + else + { + throw JsonReaderException.Create(this, "Error parsing undefined value."); + } + } + + private void ParseFalse() + { + if (MatchValueWithTrailingSeparator(JsonConvert.False)) + { + SetToken(JsonToken.Boolean, false); + } + else + { + throw JsonReaderException.Create(this, "Error parsing boolean value."); + } + } + + private void ParseNumberNegativeInfinity() + { + if (MatchValueWithTrailingSeparator(JsonConvert.NegativeInfinity)) + { + if (_floatParseHandling == FloatParseHandling.Decimal) + throw new JsonReaderException("Cannot read -Infinity as a decimal."); + + SetToken(JsonToken.Float, double.NegativeInfinity); + } + else + { + throw JsonReaderException.Create(this, "Error parsing negative infinity value."); + } + } + + private void ParseNumberPositiveInfinity() + { + if (MatchValueWithTrailingSeparator(JsonConvert.PositiveInfinity)) + { + if (_floatParseHandling == FloatParseHandling.Decimal) + throw new JsonReaderException("Cannot read Infinity as a decimal."); + + SetToken(JsonToken.Float, double.PositiveInfinity); + } + else + { + throw JsonReaderException.Create(this, "Error parsing positive infinity value."); + } + } + + private void ParseNumberNaN() + { + if (MatchValueWithTrailingSeparator(JsonConvert.NaN)) + { + if (_floatParseHandling == FloatParseHandling.Decimal) + throw new JsonReaderException("Cannot read NaN as a decimal."); + + SetToken(JsonToken.Float, double.NaN); + } + else + { + throw JsonReaderException.Create(this, "Error parsing NaN value."); + } + } + + /// + /// Changes the state to closed. + /// + public override void Close() + { + base.Close(); + + if (CloseInput && _reader != null) +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + _reader.Close(); +#else + _reader.Dispose(); +#endif + + if (_buffer != null) + _buffer.Clear(); + } + + /// + /// Gets a value indicating whether the class can return line information. + /// + /// + /// true if LineNumber and LinePosition can be provided; otherwise, false. + /// + public bool HasLineInfo() + { + return true; + } + + /// + /// Gets the current line number. + /// + /// + /// The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + /// + public int LineNumber + { + get + { + if (CurrentState == State.Start && LinePosition == 0) + return 0; + + return _lineNumber; + } + } + + /// + /// Gets the current line position. + /// + /// + /// The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + /// + public int LinePosition + { + get { return _charPos - _lineStartPos; } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonTextWriter.cs b/Libs/JsonNet/JsonTextWriter.cs new file mode 100644 index 0000000..7a48fec --- /dev/null +++ b/Libs/JsonNet/JsonTextWriter.cs @@ -0,0 +1,758 @@ +#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.Globalization; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +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 readonly TextWriter _writer; + private Base64Encoder _base64Encoder; + private char _indentChar; + private int _indentation; + private char _quoteChar; + private bool _quoteName; + private bool[] _charEscapeFlags; + private char[] _writeBuffer; + private char[] _indentChars; + + private Base64Encoder Base64Encoder + { + get + { + if (_base64Encoder == null) + _base64Encoder = new Base64Encoder(_writer); + + return _base64Encoder; + } + } + + /// + /// 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; + UpdateCharEscapeFlags(); + } + } + + /// + /// Gets or sets which character to use for indenting when is set to Formatting.Indented. + /// + public char IndentChar + { + get { return _indentChar; } + set + { + if (value != _indentChar) + { + _indentChar = value; + _indentChars = null; + } + } + } + + /// + /// 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; + + UpdateCharEscapeFlags(); + } + + /// + /// 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(); + + if (CloseOutput && _writer != null) +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + _writer.Close(); +#else + _writer.Dispose(); +#endif + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + InternalWriteStart(JsonToken.StartObject, JsonContainerType.Object); + + _writer.Write('{'); + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + InternalWriteStart(JsonToken.StartArray, JsonContainerType.Array); + + _writer.Write('['); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + InternalWriteStart(JsonToken.StartConstructor, JsonContainerType.Constructor); + + _writer.Write("new "); + _writer.Write(name); + _writer.Write('('); + } + + /// + /// Writes the specified end token. + /// + /// The end token to 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 JsonWriterException.Create(this, "Invalid JsonToken: " + token, null); + } + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + InternalWritePropertyName(name); + + WriteEscapedString(name, _quoteName); + + _writer.Write(':'); + } + + /// + /// Writes the property name of a name/value pair on a JSON object. + /// + /// The name of the property. + /// A flag to indicate whether the text should be escaped when it is written as a JSON property name. + public override void WritePropertyName(string name, bool escape) + { + InternalWritePropertyName(name); + + if (escape) + { + WriteEscapedString(name, _quoteName); + } + else + { + if (_quoteName) + _writer.Write(_quoteChar); + + _writer.Write(name); + + if (_quoteName) + _writer.Write(_quoteChar); + } + + _writer.Write(':'); + } + + internal override void OnStringEscapeHandlingChanged() + { + UpdateCharEscapeFlags(); + } + + private void UpdateCharEscapeFlags() + { + _charEscapeFlags = JavaScriptUtils.GetCharEscapeFlags(StringEscapeHandling, _quoteChar); + } + + /// + /// Writes indent characters. + /// + protected override void WriteIndent() + { + _writer.WriteLine(); + + // levels of indentation multiplied by the indent count + int currentIndentCount = Top * _indentation; + + if (currentIndentCount > 0) + { + if (_indentChars == null) + _indentChars = new string(_indentChar, 10).ToCharArray(); + + while (currentIndentCount > 0) + { + int writeCount = Math.Min(currentIndentCount, 10); + + _writer.Write(_indentChars, 0, writeCount); + + currentIndentCount -= writeCount; + } + } + } + + /// + /// Writes the JSON value delimiter. + /// + protected override void WriteValueDelimiter() + { + _writer.Write(','); + } + + /// + /// Writes an indent space. + /// + protected override void WriteIndentSpace() + { + _writer.Write(' '); + } + + private void WriteValueInternal(string value, JsonToken token) + { + _writer.Write(value); + } + + #region WriteValue methods + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public override void WriteValue(object value) + { +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + InternalWriteValue(JsonToken.Integer); + WriteValueInternal(((BigInteger)value).ToString(CultureInfo.InvariantCulture), JsonToken.String); + } + else +#endif + { + base.WriteValue(value); + } + } + + /// + /// Writes a null value. + /// + public override void WriteNull() + { + InternalWriteValue(JsonToken.Null); + WriteValueInternal(JsonConvert.Null, JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + InternalWriteValue(JsonToken.Undefined); + WriteValueInternal(JsonConvert.Undefined, JsonToken.Undefined); + } + + /// + /// Writes raw JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + InternalWriteRaw(); + + _writer.Write(json); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + InternalWriteValue(JsonToken.String); + + if (value == null) + WriteValueInternal(JsonConvert.Null, JsonToken.Null); + else + WriteEscapedString(value, true); + } + + private void WriteEscapedString(string value, bool quote) + { + EnsureWriteBuffer(); + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, quote, _charEscapeFlags, StringEscapeHandling, ref _writeBuffer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(uint value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ulong value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + InternalWriteValue(JsonToken.Float); + WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float? value) + { + if (value == null) + { + WriteNull(); + } + else + { + InternalWriteValue(JsonToken.Float); + WriteValueInternal(JsonConvert.ToString(value.Value, FloatFormatHandling, QuoteChar, true), JsonToken.Float); + } + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + InternalWriteValue(JsonToken.Float); + WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double? value) + { + if (value == null) + { + WriteNull(); + } + else + { + InternalWriteValue(JsonToken.Float); + WriteValueInternal(JsonConvert.ToString(value.Value, FloatFormatHandling, QuoteChar, true), JsonToken.Float); + } + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + InternalWriteValue(JsonToken.Boolean); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ushort value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + InternalWriteValue(JsonToken.String); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(sbyte value) + { + InternalWriteValue(JsonToken.Integer); + WriteIntegerValue(value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + InternalWriteValue(JsonToken.Float); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + InternalWriteValue(JsonToken.Date); + value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling); + + if (string.IsNullOrEmpty(DateFormatString)) + { + EnsureWriteBuffer(); + + int pos = 0; + _writeBuffer[pos++] = _quoteChar; + pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, value, null, value.Kind, DateFormatHandling); + _writeBuffer[pos++] = _quoteChar; + + _writer.Write(_writeBuffer, 0, pos); + } + else + { + _writer.Write(_quoteChar); + _writer.Write(value.ToString(DateFormatString, Culture)); + _writer.Write(_quoteChar); + } + } + + /// + /// Writes a [] value. + /// + /// The [] value to write. + public override void WriteValue(byte[] value) + { + if (value == null) + { + WriteNull(); + } + else + { + InternalWriteValue(JsonToken.Bytes); + _writer.Write(_quoteChar); + Base64Encoder.Encode(value, 0, value.Length); + Base64Encoder.Flush(); + _writer.Write(_quoteChar); + } + } + +#if !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + InternalWriteValue(JsonToken.Date); + + if (string.IsNullOrEmpty(DateFormatString)) + { + EnsureWriteBuffer(); + + int pos = 0; + _writeBuffer[pos++] = _quoteChar; + pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, (DateFormatHandling == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, DateFormatHandling); + _writeBuffer[pos++] = _quoteChar; + + _writer.Write(_writeBuffer, 0, pos); + } + else + { + _writer.Write(_quoteChar); + _writer.Write(value.ToString(DateFormatString, Culture)); + _writer.Write(_quoteChar); + } + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Guid value) + { + InternalWriteValue(JsonToken.String); + + string text = null; + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + text = value.ToString("D", CultureInfo.InvariantCulture); +#else + text = value.ToString("D"); +#endif + + _writer.Write(_quoteChar); + _writer.Write(text); + _writer.Write(_quoteChar); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(TimeSpan value) + { + InternalWriteValue(JsonToken.String); + + string text; +#if (NET35 || NET20) + text = value.ToString(); +#else + text = value.ToString(null, CultureInfo.InvariantCulture); +#endif + + _writer.Write(_quoteChar); + _writer.Write(text); + _writer.Write(_quoteChar); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Uri value) + { + if (value == null) + { + WriteNull(); + } + else + { + InternalWriteValue(JsonToken.String); + WriteEscapedString(value.OriginalString, true); + } + } + #endregion + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + InternalWriteComment(); + + _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) + { + InternalWriteWhitespace(ws); + + _writer.Write(ws); + } + + private void EnsureWriteBuffer() + { + if (_writeBuffer == null) + _writeBuffer = new char[35]; // maximum buffer sized used when writing iso date + } + + private void WriteIntegerValue(long value) + { + if (value >= 0 && value <= 9) + { + _writer.Write((char)('0' + value)); + } + else + { + ulong uvalue = (value < 0) ? (ulong)-value : (ulong)value; + + if (value < 0) + _writer.Write('-'); + + WriteIntegerValue(uvalue); + } + } + + private void WriteIntegerValue(ulong uvalue) + { + if (uvalue <= 9) + { + _writer.Write((char)('0' + uvalue)); + } + else + { + EnsureWriteBuffer(); + + int totalLength = MathUtils.IntLength(uvalue); + int length = 0; + + do + { + _writeBuffer[totalLength - ++length] = (char)('0' + (uvalue % 10)); + uvalue /= 10; + } while (uvalue != 0); + + _writer.Write(_writeBuffer, 0, length); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonToken.cs b/Libs/JsonNet/JsonToken.cs new file mode 100644 index 0000000..a159902 --- /dev/null +++ b/Libs/JsonNet/JsonToken.cs @@ -0,0 +1,127 @@ +#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 +{ + /// + /// Specifies the type of Json token. + /// + public enum JsonToken + { + /// + /// This is returned by the if a method has not been called. + /// + None, + + /// + /// An object start token. + /// + StartObject, + + /// + /// An array start token. + /// + StartArray, + + /// + /// A constructor start token. + /// + StartConstructor, + + /// + /// An object property name. + /// + PropertyName, + + /// + /// A comment. + /// + Comment, + + /// + /// Raw JSON. + /// + Raw, + + /// + /// An integer. + /// + Integer, + + /// + /// A float. + /// + Float, + + /// + /// A string. + /// + String, + + /// + /// A boolean. + /// + Boolean, + + /// + /// A null token. + /// + Null, + + /// + /// An undefined token. + /// + Undefined, + + /// + /// An object end token. + /// + EndObject, + + /// + /// An array end token. + /// + EndArray, + + /// + /// A constructor end token. + /// + EndConstructor, + + /// + /// A Date. + /// + Date, + + /// + /// Byte data. + /// + Bytes + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonValidatingReader.cs b/Libs/JsonNet/JsonValidatingReader.cs new file mode 100644 index 0000000..a02d2de --- /dev/null +++ b/Libs/JsonNet/JsonValidatingReader.cs @@ -0,0 +1,916 @@ +#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; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Utilities; +using System.Globalization; +using System.Text.RegularExpressions; +using System.IO; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides validation. + /// + public class JsonValidatingReader : JsonReader, IJsonLineInfo + { + private class SchemaScope + { + private readonly JTokenType _tokenType; + private readonly IList _schemas; + private readonly Dictionary _requiredProperties; + + public string CurrentPropertyName { get; set; } + public int ArrayItemCount { get; set; } + public bool IsUniqueArray { get; set; } + public IList UniqueArrayItems { get; set; } + public JTokenWriter CurrentItemWriter { get; set; } + + public IList Schemas + { + get { return _schemas; } + } + + public Dictionary RequiredProperties + { + get { return _requiredProperties; } + } + + public JTokenType TokenType + { + get { return _tokenType; } + } + + public SchemaScope(JTokenType tokenType, IList schemas) + { + _tokenType = tokenType; + _schemas = schemas; + + _requiredProperties = schemas.SelectMany(GetRequiredProperties).Distinct().ToDictionary(p => p, p => false); + + if (tokenType == JTokenType.Array && schemas.Any(s => s.UniqueItems)) + { + IsUniqueArray = true; + UniqueArrayItems = new List(); + } + } + + private IEnumerable GetRequiredProperties(JsonSchemaModel schema) + { + if (schema == null || schema.Properties == null) + return Enumerable.Empty(); + + return schema.Properties.Where(p => p.Value.Required).Select(p => p.Key); + } + } + + private readonly JsonReader _reader; + private readonly Stack _stack; + private JsonSchema _schema; + private JsonSchemaModel _model; + private SchemaScope _currentScope; + + /// + /// Sets an event handler for receiving schema validation errors. + /// + public event ValidationEventHandler ValidationEventHandler; + + /// + /// Gets the text value of the current JSON token. + /// + /// + public override object Value + { + get { return _reader.Value; } + } + + /// + /// Gets the depth of the current token in the JSON document. + /// + /// The depth of the current token in the JSON document. + public override int Depth + { + get { return _reader.Depth; } + } + + /// + /// Gets the path of the current JSON token. + /// + public override string Path + { + get { return _reader.Path; } + } + + /// + /// Gets the quotation mark character used to enclose the value of a string. + /// + /// + public override char QuoteChar + { + get { return _reader.QuoteChar; } + protected internal set { } + } + + /// + /// Gets the type of the current JSON token. + /// + /// + public override JsonToken TokenType + { + get { return _reader.TokenType; } + } + + /// + /// Gets the Common Language Runtime (CLR) type for the current JSON token. + /// + /// + public override Type ValueType + { + get { return _reader.ValueType; } + } + + private void Push(SchemaScope scope) + { + _stack.Push(scope); + _currentScope = scope; + } + + private SchemaScope Pop() + { + SchemaScope poppedScope = _stack.Pop(); + _currentScope = (_stack.Count != 0) + ? _stack.Peek() + : null; + + return poppedScope; + } + + private IList CurrentSchemas + { + get { return _currentScope.Schemas; } + } + + private static readonly IList EmptySchemaList = new List(); + + private IList CurrentMemberSchemas + { + get + { + if (_currentScope == null) + return new List(new[] { _model }); + + if (_currentScope.Schemas == null || _currentScope.Schemas.Count == 0) + return EmptySchemaList; + + switch (_currentScope.TokenType) + { + case JTokenType.None: + return _currentScope.Schemas; + case JTokenType.Object: + { + if (_currentScope.CurrentPropertyName == null) + throw new JsonReaderException("CurrentPropertyName has not been set on scope."); + + IList schemas = new List(); + + foreach (JsonSchemaModel schema in CurrentSchemas) + { + JsonSchemaModel propertySchema; + if (schema.Properties != null && schema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema)) + { + schemas.Add(propertySchema); + } + if (schema.PatternProperties != null) + { + foreach (KeyValuePair patternProperty in schema.PatternProperties) + { + if (Regex.IsMatch(_currentScope.CurrentPropertyName, patternProperty.Key)) + { + schemas.Add(patternProperty.Value); + } + } + } + + if (schemas.Count == 0 && schema.AllowAdditionalProperties && schema.AdditionalProperties != null) + schemas.Add(schema.AdditionalProperties); + } + + return schemas; + } + case JTokenType.Array: + { + IList schemas = new List(); + + foreach (JsonSchemaModel schema in CurrentSchemas) + { + if (!schema.PositionalItemsValidation) + { + if (schema.Items != null && schema.Items.Count > 0) + schemas.Add(schema.Items[0]); + } + else + { + if (schema.Items != null && schema.Items.Count > 0) + { + if (schema.Items.Count > (_currentScope.ArrayItemCount - 1)) + schemas.Add(schema.Items[_currentScope.ArrayItemCount - 1]); + } + + if (schema.AllowAdditionalItems && schema.AdditionalItems != null) + schemas.Add(schema.AdditionalItems); + } + } + + return schemas; + } + case JTokenType.Constructor: + return EmptySchemaList; + default: + throw new ArgumentOutOfRangeException("TokenType", "Unexpected token type: {0}".FormatWith(CultureInfo.InvariantCulture, _currentScope.TokenType)); + } + } + } + + private void RaiseError(string message, JsonSchemaModel schema) + { + IJsonLineInfo lineInfo = this; + + string exceptionMessage = (lineInfo.HasLineInfo()) + ? message + " Line {0}, position {1}.".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition) + : message; + + OnValidationEvent(new JsonSchemaException(exceptionMessage, null, Path, lineInfo.LineNumber, lineInfo.LinePosition)); + } + + private void OnValidationEvent(JsonSchemaException exception) + { + ValidationEventHandler handler = ValidationEventHandler; + if (handler != null) + handler(this, new ValidationEventArgs(exception)); + else + throw exception; + } + + /// + /// Initializes a new instance of the class that + /// validates the content returned from the given . + /// + /// The to read from while validating. + public JsonValidatingReader(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + _reader = reader; + _stack = new Stack(); + } + + /// + /// Gets or sets the schema. + /// + /// The schema. + public JsonSchema Schema + { + get { return _schema; } + set + { + if (TokenType != JsonToken.None) + throw new InvalidOperationException("Cannot change schema while validating JSON."); + + _schema = value; + _model = null; + } + } + + /// + /// Gets the used to construct this . + /// + /// The specified in the constructor. + public JsonReader Reader + { + get { return _reader; } + } + + private void ValidateNotDisallowed(JsonSchemaModel schema) + { + if (schema == null) + return; + + JsonSchemaType? currentNodeType = GetCurrentNodeSchemaType(); + if (currentNodeType != null) + { + if (JsonSchemaGenerator.HasFlag(schema.Disallow, currentNodeType.Value)) + RaiseError("Type {0} is disallowed.".FormatWith(CultureInfo.InvariantCulture, currentNodeType), schema); + } + } + + private JsonSchemaType? GetCurrentNodeSchemaType() + { + switch (_reader.TokenType) + { + case JsonToken.StartObject: + return JsonSchemaType.Object; + case JsonToken.StartArray: + return JsonSchemaType.Array; + case JsonToken.Integer: + return JsonSchemaType.Integer; + case JsonToken.Float: + return JsonSchemaType.Float; + case JsonToken.String: + return JsonSchemaType.String; + case JsonToken.Boolean: + return JsonSchemaType.Boolean; + case JsonToken.Null: + return JsonSchemaType.Null; + default: + return null; + } + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override int? ReadAsInt32() + { + int? i = _reader.ReadAsInt32(); + + ValidateCurrentToken(); + return i; + } + + /// + /// Reads the next JSON token from the stream as a []. + /// + /// + /// A [] or a null reference if the next JSON token is null. + /// + public override byte[] ReadAsBytes() + { + byte[] data = _reader.ReadAsBytes(); + + ValidateCurrentToken(); + return data; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override decimal? ReadAsDecimal() + { + decimal? d = _reader.ReadAsDecimal(); + + ValidateCurrentToken(); + return d; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override string ReadAsString() + { + string s = _reader.ReadAsString(); + + ValidateCurrentToken(); + return s; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTime? ReadAsDateTime() + { + DateTime? dateTime = _reader.ReadAsDateTime(); + + ValidateCurrentToken(); + return dateTime; + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override DateTimeOffset? ReadAsDateTimeOffset() + { + DateTimeOffset? dateTimeOffset = _reader.ReadAsDateTimeOffset(); + + ValidateCurrentToken(); + return dateTimeOffset; + } +#endif + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + if (!_reader.Read()) + return false; + + if (_reader.TokenType == JsonToken.Comment) + return true; + + ValidateCurrentToken(); + return true; + } + + private void ValidateCurrentToken() + { + // first time validate has been called. build model + if (_model == null) + { + JsonSchemaModelBuilder builder = new JsonSchemaModelBuilder(); + _model = builder.Build(_schema); + + if (!JsonWriter.IsStartToken(_reader.TokenType)) + Push(new SchemaScope(JTokenType.None, CurrentMemberSchemas)); + } + + switch (_reader.TokenType) + { + case JsonToken.StartObject: + ProcessValue(); + IList objectSchemas = CurrentMemberSchemas.Where(ValidateObject).ToList(); + Push(new SchemaScope(JTokenType.Object, objectSchemas)); + WriteToken(CurrentSchemas); + break; + case JsonToken.StartArray: + ProcessValue(); + IList arraySchemas = CurrentMemberSchemas.Where(ValidateArray).ToList(); + Push(new SchemaScope(JTokenType.Array, arraySchemas)); + WriteToken(CurrentSchemas); + break; + case JsonToken.StartConstructor: + ProcessValue(); + Push(new SchemaScope(JTokenType.Constructor, null)); + WriteToken(CurrentSchemas); + break; + case JsonToken.PropertyName: + WriteToken(CurrentSchemas); + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidatePropertyName(schema); + } + break; + case JsonToken.Raw: + ProcessValue(); + break; + case JsonToken.Integer: + ProcessValue(); + WriteToken(CurrentMemberSchemas); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateInteger(schema); + } + break; + case JsonToken.Float: + ProcessValue(); + WriteToken(CurrentMemberSchemas); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateFloat(schema); + } + break; + case JsonToken.String: + ProcessValue(); + WriteToken(CurrentMemberSchemas); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateString(schema); + } + break; + case JsonToken.Boolean: + ProcessValue(); + WriteToken(CurrentMemberSchemas); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateBoolean(schema); + } + break; + case JsonToken.Null: + ProcessValue(); + WriteToken(CurrentMemberSchemas); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateNull(schema); + } + break; + case JsonToken.EndObject: + WriteToken(CurrentSchemas); + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidateEndObject(schema); + } + Pop(); + break; + case JsonToken.EndArray: + WriteToken(CurrentSchemas); + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidateEndArray(schema); + } + Pop(); + break; + case JsonToken.EndConstructor: + WriteToken(CurrentSchemas); + Pop(); + break; + case JsonToken.Undefined: + case JsonToken.Date: + case JsonToken.Bytes: + // these have no equivalent in JSON schema + WriteToken(CurrentMemberSchemas); + break; + case JsonToken.None: + // no content, do nothing + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void WriteToken(IList schemas) + { + foreach (SchemaScope schemaScope in _stack) + { + bool isInUniqueArray = (schemaScope.TokenType == JTokenType.Array && schemaScope.IsUniqueArray && schemaScope.ArrayItemCount > 0); + + if (isInUniqueArray || schemas.Any(s => s.Enum != null)) + { + if (schemaScope.CurrentItemWriter == null) + { + if (JsonWriter.IsEndToken(_reader.TokenType)) + continue; + + schemaScope.CurrentItemWriter = new JTokenWriter(); + } + + schemaScope.CurrentItemWriter.WriteToken(_reader, false); + + // finished writing current item + if (schemaScope.CurrentItemWriter.Top == 0 && _reader.TokenType != JsonToken.PropertyName) + { + JToken finishedItem = schemaScope.CurrentItemWriter.Token; + + // start next item with new writer + schemaScope.CurrentItemWriter = null; + + if (isInUniqueArray) + { + if (schemaScope.UniqueArrayItems.Contains(finishedItem, JToken.EqualityComparer)) + RaiseError("Non-unique array item at index {0}.".FormatWith(CultureInfo.InvariantCulture, schemaScope.ArrayItemCount - 1), schemaScope.Schemas.First(s => s.UniqueItems)); + + schemaScope.UniqueArrayItems.Add(finishedItem); + } + else if (schemas.Any(s => s.Enum != null)) + { + foreach (JsonSchemaModel schema in schemas) + { + if (schema.Enum != null) + { + if (!schema.Enum.ContainsValue(finishedItem, JToken.EqualityComparer)) + { + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + finishedItem.WriteTo(new JsonTextWriter(sw)); + + RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()), schema); + } + } + } + } + } + } + } + } + + private void ValidateEndObject(JsonSchemaModel schema) + { + if (schema == null) + return; + + Dictionary requiredProperties = _currentScope.RequiredProperties; + + if (requiredProperties != null) + { + List unmatchedRequiredProperties = + requiredProperties.Where(kv => !kv.Value).Select(kv => kv.Key).ToList(); + + if (unmatchedRequiredProperties.Count > 0) + RaiseError("Required properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema); + } + } + + private void ValidateEndArray(JsonSchemaModel schema) + { + if (schema == null) + return; + + int arrayItemCount = _currentScope.ArrayItemCount; + + if (schema.MaximumItems != null && arrayItemCount > schema.MaximumItems) + RaiseError("Array item count {0} exceeds maximum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MaximumItems), schema); + + if (schema.MinimumItems != null && arrayItemCount < schema.MinimumItems) + RaiseError("Array item count {0} is less than minimum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MinimumItems), schema); + } + + private void ValidateNull(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Null)) + return; + + ValidateNotDisallowed(schema); + } + + private void ValidateBoolean(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Boolean)) + return; + + ValidateNotDisallowed(schema); + } + + private void ValidateString(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.String)) + return; + + ValidateNotDisallowed(schema); + + string value = _reader.Value.ToString(); + + if (schema.MaximumLength != null && value.Length > schema.MaximumLength) + RaiseError("String '{0}' exceeds maximum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MaximumLength), schema); + + if (schema.MinimumLength != null && value.Length < schema.MinimumLength) + RaiseError("String '{0}' is less than minimum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MinimumLength), schema); + + if (schema.Patterns != null) + { + foreach (string pattern in schema.Patterns) + { + if (!Regex.IsMatch(value, pattern)) + RaiseError("String '{0}' does not match regex pattern '{1}'.".FormatWith(CultureInfo.InvariantCulture, value, pattern), schema); + } + } + } + + private void ValidateInteger(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Integer)) + return; + + ValidateNotDisallowed(schema); + + object value = _reader.Value; + + if (schema.Maximum != null) + { + if (JValue.Compare(JTokenType.Integer, value, schema.Maximum) > 0) + RaiseError("Integer {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema); + if (schema.ExclusiveMaximum && JValue.Compare(JTokenType.Integer, value, schema.Maximum) == 0) + RaiseError("Integer {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema); + } + + if (schema.Minimum != null) + { + if (JValue.Compare(JTokenType.Integer, value, schema.Minimum) < 0) + RaiseError("Integer {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema); + if (schema.ExclusiveMinimum && JValue.Compare(JTokenType.Integer, value, schema.Minimum) == 0) + RaiseError("Integer {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema); + } + + if (schema.DivisibleBy != null) + { + bool notDivisible; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (value is BigInteger) + { + // not that this will lose any decimal point on DivisibleBy + // so manually raise an error if DivisibleBy is not an integer and value is not zero + BigInteger i = (BigInteger)value; + bool divisibleNonInteger = !Math.Abs(schema.DivisibleBy.Value - Math.Truncate(schema.DivisibleBy.Value)).Equals(0); + if (divisibleNonInteger) + notDivisible = i != 0; + else + notDivisible = i % new BigInteger(schema.DivisibleBy.Value) != 0; + } + else +#endif + notDivisible = !IsZero(Convert.ToInt64(value, CultureInfo.InvariantCulture) % schema.DivisibleBy.Value); + + if (notDivisible) + RaiseError("Integer {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema); + } + } + + private void ProcessValue() + { + if (_currentScope != null && _currentScope.TokenType == JTokenType.Array) + { + _currentScope.ArrayItemCount++; + + foreach (JsonSchemaModel currentSchema in CurrentSchemas) + { + // if there is positional validation and the array index is past the number of item validation schemas and there is no additonal items then error + if (currentSchema != null + && currentSchema.PositionalItemsValidation + && !currentSchema.AllowAdditionalItems + && (currentSchema.Items == null || _currentScope.ArrayItemCount - 1 >= currentSchema.Items.Count)) + { + RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), currentSchema); + } + } + } + } + + private void ValidateFloat(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Float)) + return; + + ValidateNotDisallowed(schema); + + double value = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); + + if (schema.Maximum != null) + { + if (value > schema.Maximum) + RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); + if (schema.ExclusiveMaximum && value == schema.Maximum) + RaiseError("Float {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); + } + + if (schema.Minimum != null) + { + if (value < schema.Minimum) + RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); + if (schema.ExclusiveMinimum && value == schema.Minimum) + RaiseError("Float {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); + } + + if (schema.DivisibleBy != null) + { + double remainder = FloatingPointRemainder(value, schema.DivisibleBy.Value); + + if (!IsZero(remainder)) + RaiseError("Float {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema); + } + } + + private static double FloatingPointRemainder(double dividend, double divisor) + { + return dividend - Math.Floor(dividend / divisor) * divisor; + } + + private static bool IsZero(double value) + { + const double epsilon = 2.2204460492503131e-016; + + return Math.Abs(value) < 20.0 * epsilon; + } + + private void ValidatePropertyName(JsonSchemaModel schema) + { + if (schema == null) + return; + + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + + if (_currentScope.RequiredProperties.ContainsKey(propertyName)) + _currentScope.RequiredProperties[propertyName] = true; + + if (!schema.AllowAdditionalProperties) + { + bool propertyDefinied = IsPropertyDefinied(schema, propertyName); + + if (!propertyDefinied) + RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema); + } + + _currentScope.CurrentPropertyName = propertyName; + } + + private bool IsPropertyDefinied(JsonSchemaModel schema, string propertyName) + { + if (schema.Properties != null && schema.Properties.ContainsKey(propertyName)) + return true; + + if (schema.PatternProperties != null) + { + foreach (string pattern in schema.PatternProperties.Keys) + { + if (Regex.IsMatch(propertyName, pattern)) + return true; + } + } + + return false; + } + + private bool ValidateArray(JsonSchemaModel schema) + { + if (schema == null) + return true; + + return (TestType(schema, JsonSchemaType.Array)); + } + + private bool ValidateObject(JsonSchemaModel schema) + { + if (schema == null) + return true; + + return (TestType(schema, JsonSchemaType.Object)); + } + + private bool TestType(JsonSchemaModel currentSchema, JsonSchemaType currentType) + { + if (!JsonSchemaGenerator.HasFlag(currentSchema.Type, currentType)) + { + RaiseError("Invalid type. Expected {0} but got {1}.".FormatWith(CultureInfo.InvariantCulture, currentSchema.Type, currentType), currentSchema); + return false; + } + + return true; + } + + bool IJsonLineInfo.HasLineInfo() + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return lineInfo != null && lineInfo.HasLineInfo(); + } + + int IJsonLineInfo.LineNumber + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LineNumber : 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LinePosition : 0; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonWriter.cs b/Libs/JsonNet/JsonWriter.cs new file mode 100644 index 0000000..6f2b080 --- /dev/null +++ b/Libs/JsonNet/JsonWriter.cs @@ -0,0 +1,1526 @@ +#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.IO; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using Newtonsoft.Json.Utilities; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public abstract class JsonWriter : IDisposable + { + internal enum State + { + Start, + Property, + ObjectStart, + Object, + ArrayStart, + Array, + ConstructorStart, + Constructor, + Closed, + Error + } + + // array that gives a new state based on the current state an the token being written + private static readonly State[][] StateArray; + + internal static readonly State[][] StateArrayTempate = new[] + { + // Start PropertyName ObjectStart Object ArrayStart Array ConstructorStart Constructor Closed 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 }, + /* Property */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 (this will be copied) */new[] { State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error } + }; + + internal static State[][] BuildStateArray() + { + var allStates = StateArrayTempate.ToList(); + var errorStates = StateArrayTempate[0]; + var valueStates = StateArrayTempate[7]; + + foreach (JsonToken valueToken in EnumUtils.GetValues(typeof(JsonToken))) + { + if (allStates.Count <= (int)valueToken) + { + switch (valueToken) + { + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Undefined: + case JsonToken.Date: + case JsonToken.Bytes: + allStates.Add(valueStates); + break; + default: + allStates.Add(errorStates); + break; + } + } + } + + return allStates.ToArray(); + } + + static JsonWriter() + { + StateArray = BuildStateArray(); + } + + private readonly List _stack; + private JsonPosition _currentPosition; + private State _currentState; + private Formatting _formatting; + + /// + /// Gets or sets a value indicating whether the underlying stream or + /// should be closed when the writer is closed. + /// + /// + /// true to close the underlying stream or when + /// the writer is closed; otherwise false. The default is true. + /// + public bool CloseOutput { get; set; } + + /// + /// Gets the top. + /// + /// The top. + protected internal int Top + { + get + { + int depth = _stack.Count; + if (Peek() != JsonContainerType.None) + depth++; + + return depth; + } + } + + /// + /// Gets the state of the writer. + /// + public WriteState WriteState + { + get + { + switch (_currentState) + { + case State.Error: + return WriteState.Error; + case State.Closed: + return WriteState.Closed; + case State.Object: + case State.ObjectStart: + return WriteState.Object; + case State.Array: + case State.ArrayStart: + return WriteState.Array; + case State.Constructor: + case State.ConstructorStart: + return WriteState.Constructor; + case State.Property: + return WriteState.Property; + case State.Start: + return WriteState.Start; + default: + throw JsonWriterException.Create(this, "Invalid state: " + _currentState, null); + } + } + } + + internal string ContainerPath + { + get + { + if (_currentPosition.Type == JsonContainerType.None) + return string.Empty; + + return JsonPosition.BuildPath(_stack); + } + } + + /// + /// Gets the path of the writer. + /// + public string Path + { + get + { + if (_currentPosition.Type == JsonContainerType.None) + return string.Empty; + + bool insideContainer = (_currentState != State.ArrayStart + && _currentState != State.ConstructorStart + && _currentState != State.ObjectStart); + + IEnumerable positions = (!insideContainer) + ? _stack + : _stack.Concat(new[] { _currentPosition }); + + return JsonPosition.BuildPath(positions); + } + } + + private DateFormatHandling _dateFormatHandling; + private DateTimeZoneHandling _dateTimeZoneHandling; + private StringEscapeHandling _stringEscapeHandling; + private FloatFormatHandling _floatFormatHandling; + private string _dateFormatString; + private CultureInfo _culture; + + /// + /// Indicates how JSON text output is formatted. + /// + public Formatting Formatting + { + get { return _formatting; } + set { _formatting = value; } + } + + /// + /// Get or set how dates are written to JSON text. + /// + public DateFormatHandling DateFormatHandling + { + get { return _dateFormatHandling; } + set { _dateFormatHandling = value; } + } + + /// + /// Get or set how time zones are handling when writing JSON text. + /// + public DateTimeZoneHandling DateTimeZoneHandling + { + get { return _dateTimeZoneHandling; } + set { _dateTimeZoneHandling = value; } + } + + /// + /// Get or set how strings are escaped when writing JSON text. + /// + public StringEscapeHandling StringEscapeHandling + { + get { return _stringEscapeHandling; } + set + { + _stringEscapeHandling = value; + OnStringEscapeHandlingChanged(); + } + } + + internal virtual void OnStringEscapeHandlingChanged() + { + // hacky but there is a calculated value that relies on StringEscapeHandling + } + + /// + /// Get or set how special floating point numbers, e.g. , + /// and , + /// are written to JSON text. + /// + public FloatFormatHandling FloatFormatHandling + { + get { return _floatFormatHandling; } + set { _floatFormatHandling = value; } + } + + /// + /// Get or set how and values are formatting when writing JSON text. + /// + public string DateFormatString + { + get { return _dateFormatString; } + set { _dateFormatString = value; } + } + + /// + /// Gets or sets the culture used when writing JSON. Defaults to . + /// + public CultureInfo Culture + { + get { return _culture ?? CultureInfo.InvariantCulture; } + set { _culture = value; } + } + + /// + /// Creates an instance of the JsonWriter class. + /// + protected JsonWriter() + { + _stack = new List(4); + _currentState = State.Start; + _formatting = Formatting.None; + _dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + + CloseOutput = true; + } + + internal void UpdateScopeWithFinishedValue() + { + if (_currentPosition.HasIndex) + _currentPosition.Position++; + } + + private void Push(JsonContainerType value) + { + if (_currentPosition.Type != JsonContainerType.None) + _stack.Add(_currentPosition); + + _currentPosition = new JsonPosition(value); + } + + private JsonContainerType Pop() + { + JsonPosition oldPosition = _currentPosition; + + if (_stack.Count > 0) + { + _currentPosition = _stack[_stack.Count - 1]; + _stack.RemoveAt(_stack.Count - 1); + } + else + { + _currentPosition = new JsonPosition(); + } + + return oldPosition.Type; + } + + private JsonContainerType Peek() + { + return _currentPosition.Type; + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public abstract void Flush(); + + /// + /// Closes this stream and the underlying stream. + /// + public virtual void Close() + { + AutoCompleteAll(); + } + + /// + /// Writes the beginning of a Json object. + /// + public virtual void WriteStartObject() + { + InternalWriteStart(JsonToken.StartObject, JsonContainerType.Object); + } + + /// + /// Writes the end of a Json object. + /// + public virtual void WriteEndObject() + { + InternalWriteEnd(JsonContainerType.Object); + } + + /// + /// Writes the beginning of a Json array. + /// + public virtual void WriteStartArray() + { + InternalWriteStart(JsonToken.StartArray, JsonContainerType.Array); + } + + /// + /// Writes the end of an array. + /// + public virtual void WriteEndArray() + { + InternalWriteEnd(JsonContainerType.Array); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public virtual void WriteStartConstructor(string name) + { + InternalWriteStart(JsonToken.StartConstructor, JsonContainerType.Constructor); + } + + /// + /// Writes the end constructor. + /// + public virtual void WriteEndConstructor() + { + InternalWriteEnd(JsonContainerType.Constructor); + } + + /// + /// Writes the property name of a name/value pair on a JSON object. + /// + /// The name of the property. + public virtual void WritePropertyName(string name) + { + InternalWritePropertyName(name); + } + + /// + /// Writes the property name of a name/value pair on a JSON object. + /// + /// The name of the property. + /// A flag to indicate whether the text should be escaped when it is written as a JSON property name. + public virtual void WritePropertyName(string name, bool escape) + { + WritePropertyName(name); + } + + /// + /// Writes the end of the current Json object or array. + /// + public virtual void WriteEnd() + { + WriteEnd(Peek()); + } + + /// + /// Writes the current token and its children. + /// + /// The to read the token from. + public void WriteToken(JsonReader reader) + { + WriteToken(reader, true, true); + } + + /// + /// Writes the current token. + /// + /// The to read the token from. + /// A flag indicating whether the current token's children should be written. + public void WriteToken(JsonReader reader, bool writeChildren) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + WriteToken(reader, writeChildren, true); + } + + internal void WriteToken(JsonReader reader, bool writeChildren, bool writeDateConstructorAsDate) + { + int initialDepth; + + if (reader.TokenType == JsonToken.None) + initialDepth = -1; + else if (!IsStartToken(reader.TokenType)) + initialDepth = reader.Depth + 1; + else + initialDepth = reader.Depth; + + WriteToken(reader, initialDepth, writeChildren, writeDateConstructorAsDate); + } + + internal void WriteToken(JsonReader reader, int initialDepth, bool writeChildren, bool writeDateConstructorAsDate) + { + do + { + switch (reader.TokenType) + { + case JsonToken.None: + // read to next + break; + case JsonToken.StartObject: + WriteStartObject(); + break; + case JsonToken.StartArray: + WriteStartArray(); + break; + case JsonToken.StartConstructor: + string constructorName = reader.Value.ToString(); + // write a JValue date when the constructor is for a date + if (writeDateConstructorAsDate && string.Equals(constructorName, "Date", StringComparison.Ordinal)) + WriteConstructorDate(reader); + else + WriteStartConstructor(reader.Value.ToString()); + break; + case JsonToken.PropertyName: + WritePropertyName(reader.Value.ToString()); + break; + case JsonToken.Comment: + WriteComment((reader.Value != null) ? reader.Value.ToString() : null); + break; + case JsonToken.Integer: +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (reader.Value is BigInteger) + { + WriteValue((BigInteger)reader.Value); + } + else +#endif + { + WriteValue(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); + } + break; + case JsonToken.Float: + object value = reader.Value; + + if (value is decimal) + WriteValue((decimal)value); + else if (value is double) + WriteValue((double)value); + else if (value is float) + WriteValue((float)value); + else + WriteValue(Convert.ToDouble(value, CultureInfo.InvariantCulture)); + break; + case JsonToken.String: + WriteValue(reader.Value.ToString()); + break; + case JsonToken.Boolean: + WriteValue(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); + 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: +#if !NET20 + if (reader.Value is DateTimeOffset) + WriteValue((DateTimeOffset)reader.Value); + else +#endif + WriteValue(Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture)); + break; + case JsonToken.Raw: + WriteRawValue((reader.Value != null) ? reader.Value.ToString() : null); + break; + case JsonToken.Bytes: + if (reader.Value is Guid) + WriteValue((Guid)reader.Value); + else + WriteValue((byte[])reader.Value); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type."); + } + } while ( + // stop if we have reached the end of the token being read + initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0) + && writeChildren + && reader.Read()); + } + + private void WriteConstructorDate(JsonReader reader) + { + if (!reader.Read()) + throw JsonWriterException.Create(this, "Unexpected end when reading date constructor.", null); + if (reader.TokenType != JsonToken.Integer) + throw JsonWriterException.Create(this, "Unexpected token when reading date constructor. Expected Integer, got " + reader.TokenType, null); + + long ticks = (long)reader.Value; + DateTime date = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ticks); + + if (!reader.Read()) + throw JsonWriterException.Create(this, "Unexpected end when reading date constructor.", null); + if (reader.TokenType != JsonToken.EndConstructor) + throw JsonWriterException.Create(this, "Unexpected token when reading date constructor. Expected EndConstructor, got " + reader.TokenType, null); + + WriteValue(date); + } + + internal static bool IsEndToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + return true; + default: + return false; + } + } + + internal static bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + return true; + default: + return false; + } + } + + private void WriteEnd(JsonContainerType type) + { + switch (type) + { + case JsonContainerType.Object: + WriteEndObject(); + break; + case JsonContainerType.Array: + WriteEndArray(); + break; + case JsonContainerType.Constructor: + WriteEndConstructor(); + break; + default: + throw JsonWriterException.Create(this, "Unexpected type when writing end: " + type, null); + } + } + + private void AutoCompleteAll() + { + while (Top > 0) + { + WriteEnd(); + } + } + + private JsonToken GetCloseTokenForType(JsonContainerType type) + { + switch (type) + { + case JsonContainerType.Object: + return JsonToken.EndObject; + case JsonContainerType.Array: + return JsonToken.EndArray; + case JsonContainerType.Constructor: + return JsonToken.EndConstructor; + default: + throw JsonWriterException.Create(this, "No close token for type: " + type, null); + } + } + + private void AutoCompleteClose(JsonContainerType type) + { + // write closing symbol and calculate new state + int levelsToComplete = 0; + + if (_currentPosition.Type == type) + { + levelsToComplete = 1; + } + else + { + int top = Top - 2; + for (int i = top; i >= 0; i--) + { + int currentLevel = top - i; + + if (_stack[currentLevel].Type == type) + { + levelsToComplete = i + 2; + break; + } + } + } + + if (levelsToComplete == 0) + throw JsonWriterException.Create(this, "No token to close.", null); + + for (int i = 0; i < levelsToComplete; i++) + { + JsonToken token = GetCloseTokenForType(Pop()); + + if (_currentState == State.Property) + WriteNull(); + + if (_formatting == Formatting.Indented) + { + if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) + WriteIndent(); + } + + WriteEnd(token); + + JsonContainerType currentLevelType = Peek(); + + switch (currentLevelType) + { + case JsonContainerType.Object: + _currentState = State.Object; + break; + case JsonContainerType.Array: + _currentState = State.Array; + break; + case JsonContainerType.Constructor: + _currentState = State.Array; + break; + case JsonContainerType.None: + _currentState = State.Start; + break; + default: + throw JsonWriterException.Create(this, "Unknown JsonType: " + currentLevelType, null); + } + } + } + + /// + /// Writes the specified end token. + /// + /// The end token to write. + protected virtual void WriteEnd(JsonToken token) + { + } + + /// + /// Writes indent characters. + /// + protected virtual void WriteIndent() + { + } + + /// + /// Writes the JSON value delimiter. + /// + protected virtual void WriteValueDelimiter() + { + } + + /// + /// Writes an indent space. + /// + protected virtual void WriteIndentSpace() + { + } + + internal void AutoComplete(JsonToken tokenBeingWritten) + { + // gets new state based on the current state and what is being written + State newState = StateArray[(int)tokenBeingWritten][(int)_currentState]; + + if (newState == State.Error) + throw JsonWriterException.Create(this, "Token {0} in state {1} would result in an invalid JSON object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString()), null); + + if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment) + { + WriteValueDelimiter(); + } + + if (_formatting == Formatting.Indented) + { + if (_currentState == State.Property) + WriteIndentSpace(); + + // don't indent a property when it is the first token to be written (i.e. at the start) + if ((_currentState == State.Array || _currentState == State.ArrayStart || _currentState == State.Constructor || _currentState == State.ConstructorStart) + || (tokenBeingWritten == JsonToken.PropertyName && _currentState != State.Start)) + WriteIndent(); + } + + _currentState = newState; + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public virtual void WriteNull() + { + InternalWriteValue(JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public virtual void WriteUndefined() + { + InternalWriteValue(JsonToken.Undefined); + } + + /// + /// Writes raw JSON without changing the writer's state. + /// + /// The raw JSON to write. + public virtual void WriteRaw(string json) + { + InternalWriteRaw(); + } + + /// + /// Writes raw JSON where a value is expected and updates the writer's state. + /// + /// The raw JSON to write. + public virtual void WriteRawValue(string json) + { + // hack. want writer to change state as if a value had been written + UpdateScopeWithFinishedValue(); + AutoComplete(JsonToken.Undefined); + WriteRaw(json); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(string value) + { + InternalWriteValue(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(int value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(uint value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(long value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ulong value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(float value) + { + InternalWriteValue(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(double value) + { + InternalWriteValue(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(bool value) + { + InternalWriteValue(JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(short value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ushort value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(char value) + { + InternalWriteValue(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(byte value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(sbyte value) + { + InternalWriteValue(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(decimal value) + { + InternalWriteValue(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTime value) + { + InternalWriteValue(JsonToken.Date); + } + +#if !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTimeOffset value) + { + InternalWriteValue(JsonToken.Date); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(Guid value) + { + InternalWriteValue(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(TimeSpan value) + { + InternalWriteValue(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(int? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(uint? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(long? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ulong? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(float? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(double? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(bool? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(short? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ushort? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(char? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(byte? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(sbyte? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(decimal? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTime? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + +#if !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTimeOffset? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(Guid? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(TimeSpan? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a [] value. + /// + /// The [] value to write. + public virtual void WriteValue(byte[] value) + { + if (value == null) + WriteNull(); + else + InternalWriteValue(JsonToken.Bytes); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(Uri value) + { + if (value == null) + WriteNull(); + else + InternalWriteValue(JsonToken.String); + } + + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public virtual void WriteValue(object value) + { + if (value == null) + { + WriteNull(); + } + else + { +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + // this is here because adding a WriteValue(BigInteger) to JsonWriter will + // mean the user has to add a reference to System.Numerics.dll + if (value is BigInteger) + throw CreateUnsupportedTypeException(this, value); +#endif + + WriteValue(this, ConvertUtils.GetTypeCode(value.GetType()), value); + } + } + #endregion + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public virtual void WriteComment(string text) + { + InternalWriteComment(); + } + + /// + /// Writes out the given white space. + /// + /// The string of white space characters. + public virtual void WriteWhitespace(string ws) + { + InternalWriteWhitespace(ws); + } + + void IDisposable.Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (_currentState != State.Closed) + Close(); + } + + internal static void WriteValue(JsonWriter writer, PrimitiveTypeCode typeCode, object value) + { + switch (typeCode) + { + case PrimitiveTypeCode.Char: + writer.WriteValue((char)value); + break; + case PrimitiveTypeCode.CharNullable: + writer.WriteValue((value == null) ? (char?)null : (char)value); + break; + case PrimitiveTypeCode.Boolean: + writer.WriteValue((bool)value); + break; + case PrimitiveTypeCode.BooleanNullable: + writer.WriteValue((value == null) ? (bool?)null : (bool)value); + break; + case PrimitiveTypeCode.SByte: + writer.WriteValue((sbyte)value); + break; + case PrimitiveTypeCode.SByteNullable: + writer.WriteValue((value == null) ? (sbyte?)null : (sbyte)value); + break; + case PrimitiveTypeCode.Int16: + writer.WriteValue((short)value); + break; + case PrimitiveTypeCode.Int16Nullable: + writer.WriteValue((value == null) ? (short?)null : (short)value); + break; + case PrimitiveTypeCode.UInt16: + writer.WriteValue((ushort)value); + break; + case PrimitiveTypeCode.UInt16Nullable: + writer.WriteValue((value == null) ? (ushort?)null : (ushort)value); + break; + case PrimitiveTypeCode.Int32: + writer.WriteValue((int)value); + break; + case PrimitiveTypeCode.Int32Nullable: + writer.WriteValue((value == null) ? (int?)null : (int)value); + break; + case PrimitiveTypeCode.Byte: + writer.WriteValue((byte)value); + break; + case PrimitiveTypeCode.ByteNullable: + writer.WriteValue((value == null) ? (byte?)null : (byte)value); + break; + case PrimitiveTypeCode.UInt32: + writer.WriteValue((uint)value); + break; + case PrimitiveTypeCode.UInt32Nullable: + writer.WriteValue((value == null) ? (uint?)null : (uint)value); + break; + case PrimitiveTypeCode.Int64: + writer.WriteValue((long)value); + break; + case PrimitiveTypeCode.Int64Nullable: + writer.WriteValue((value == null) ? (long?)null : (long)value); + break; + case PrimitiveTypeCode.UInt64: + writer.WriteValue((ulong)value); + break; + case PrimitiveTypeCode.UInt64Nullable: + writer.WriteValue((value == null) ? (ulong?)null : (ulong)value); + break; + case PrimitiveTypeCode.Single: + writer.WriteValue((float)value); + break; + case PrimitiveTypeCode.SingleNullable: + writer.WriteValue((value == null) ? (float?)null : (float)value); + break; + case PrimitiveTypeCode.Double: + writer.WriteValue((double)value); + break; + case PrimitiveTypeCode.DoubleNullable: + writer.WriteValue((value == null) ? (double?)null : (double)value); + break; + case PrimitiveTypeCode.DateTime: + writer.WriteValue((DateTime)value); + break; + case PrimitiveTypeCode.DateTimeNullable: + writer.WriteValue((value == null) ? (DateTime?)null : (DateTime)value); + break; +#if !NET20 + case PrimitiveTypeCode.DateTimeOffset: + writer.WriteValue((DateTimeOffset)value); + break; + case PrimitiveTypeCode.DateTimeOffsetNullable: + writer.WriteValue((value == null) ? (DateTimeOffset?)null : (DateTimeOffset)value); + break; +#endif + case PrimitiveTypeCode.Decimal: + writer.WriteValue((decimal)value); + break; + case PrimitiveTypeCode.DecimalNullable: + writer.WriteValue((value == null) ? (decimal?)null : (decimal)value); + break; + case PrimitiveTypeCode.Guid: + writer.WriteValue((Guid)value); + break; + case PrimitiveTypeCode.GuidNullable: + writer.WriteValue((value == null) ? (Guid?)null : (Guid)value); + break; + case PrimitiveTypeCode.TimeSpan: + writer.WriteValue((TimeSpan)value); + break; + case PrimitiveTypeCode.TimeSpanNullable: + writer.WriteValue((value == null) ? (TimeSpan?)null : (TimeSpan)value); + break; +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) + case PrimitiveTypeCode.BigInteger: + // this will call to WriteValue(object) + writer.WriteValue((BigInteger)value); + break; + case PrimitiveTypeCode.BigIntegerNullable: + // this will call to WriteValue(object) + writer.WriteValue((value == null) ? (BigInteger?)null : (BigInteger)value); + break; +#endif + case PrimitiveTypeCode.Uri: + writer.WriteValue((Uri)value); + break; + case PrimitiveTypeCode.String: + writer.WriteValue((string)value); + break; + case PrimitiveTypeCode.Bytes: + writer.WriteValue((byte[])value); + break; +#if !(PORTABLE || NETFX_CORE) + case PrimitiveTypeCode.DBNull: + writer.WriteNull(); + break; +#endif + default: +#if !(PORTABLE || NETFX_CORE) + if (value is IConvertible) + { + // the value is a non-standard IConvertible + // convert to the underlying value and retry + IConvertible convertable = (IConvertible)value; + + TypeInformation typeInformation = ConvertUtils.GetTypeInformation(convertable); + + // if convertable has an underlying typecode of Object then attempt to convert it to a string + PrimitiveTypeCode resolvedTypeCode = (typeInformation.TypeCode == PrimitiveTypeCode.Object) ? PrimitiveTypeCode.String : typeInformation.TypeCode; + Type resolvedType = (typeInformation.TypeCode == PrimitiveTypeCode.Object) ? typeof(string) : typeInformation.Type; + + object convertedValue = convertable.ToType(resolvedType, CultureInfo.InvariantCulture); + + WriteValue(writer, resolvedTypeCode, convertedValue); + break; + } + else +#endif + { + throw CreateUnsupportedTypeException(writer, value); + } + } + } + + private static JsonWriterException CreateUnsupportedTypeException(JsonWriter writer, object value) + { + return JsonWriterException.Create(writer, "Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType()), null); + } + + /// + /// Sets the state of the JsonWriter, + /// + /// The JsonToken being written. + /// The value being written. + protected void SetWriteState(JsonToken token, object value) + { + switch (token) + { + case JsonToken.StartObject: + InternalWriteStart(token, JsonContainerType.Object); + break; + case JsonToken.StartArray: + InternalWriteStart(token, JsonContainerType.Array); + break; + case JsonToken.StartConstructor: + InternalWriteStart(token, JsonContainerType.Constructor); + break; + case JsonToken.PropertyName: + if (!(value is string)) + throw new ArgumentException("A name is required when setting property name state.", "value"); + + InternalWritePropertyName((string)value); + break; + case JsonToken.Comment: + InternalWriteComment(); + break; + case JsonToken.Raw: + InternalWriteRaw(); + break; + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Date: + case JsonToken.Bytes: + case JsonToken.Null: + case JsonToken.Undefined: + InternalWriteValue(token); + break; + case JsonToken.EndObject: + InternalWriteEnd(JsonContainerType.Object); + break; + case JsonToken.EndArray: + InternalWriteEnd(JsonContainerType.Array); + break; + case JsonToken.EndConstructor: + InternalWriteEnd(JsonContainerType.Constructor); + break; + default: + throw new ArgumentOutOfRangeException("token"); + } + } + + internal void InternalWriteEnd(JsonContainerType container) + { + AutoCompleteClose(container); + } + + internal void InternalWritePropertyName(string name) + { + _currentPosition.PropertyName = name; + AutoComplete(JsonToken.PropertyName); + } + + internal void InternalWriteRaw() + { + } + + internal void InternalWriteStart(JsonToken token, JsonContainerType container) + { + UpdateScopeWithFinishedValue(); + AutoComplete(token); + Push(container); + } + + internal void InternalWriteValue(JsonToken token) + { + UpdateScopeWithFinishedValue(); + AutoComplete(token); + } + + internal void InternalWriteWhitespace(string ws) + { + if (ws != null) + { + if (!StringUtils.IsWhiteSpace(ws)) + throw JsonWriterException.Create(this, "Only white space characters should be used.", null); + } + } + + internal void InternalWriteComment() + { + AutoComplete(JsonToken.Comment); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/JsonWriterException.cs b/Libs/JsonNet/JsonWriterException.cs new file mode 100644 index 0000000..a955e22 --- /dev/null +++ b/Libs/JsonNet/JsonWriterException.cs @@ -0,0 +1,107 @@ +#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.Runtime.Serialization; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs while reading Json text. + /// +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + [Serializable] +#endif + public class JsonWriterException : JsonException + { + /// + /// Gets the path to the JSON where the error occurred. + /// + /// The path to the JSON where the error occurred. + public string Path { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonWriterException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonWriterException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonWriterException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + public JsonWriterException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + internal JsonWriterException(string message, Exception innerException, string path) + : base(message, innerException) + { + Path = path; + } + + internal static JsonWriterException Create(JsonWriter writer, string message, Exception ex) + { + return Create(writer.ContainerPath, message, ex); + } + + internal static JsonWriterException Create(string path, string message, Exception ex) + { + message = JsonPosition.FormatMessage(null, path, message); + + return new JsonWriterException(message, ex, path); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/Extensions.cs b/Libs/JsonNet/Linq/Extensions.cs new file mode 100644 index 0000000..f74665b --- /dev/null +++ b/Libs/JsonNet/Linq/Extensions.cs @@ -0,0 +1,312 @@ +#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 Newtonsoft.Json.Utilities; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Contains the LINQ to JSON extension methods. + /// + public static class Extensions + { + /// + /// Returns a collection of tokens that contains the ancestors of every token in the source collection. + /// + /// The type of the objects in source, constrained to . + /// An of that contains the source collection. + /// An of that contains the ancestors of every node in the source collection. + public static IJEnumerable Ancestors(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(j => j.Ancestors()).AsJEnumerable(); + } + + //TODO + //public static IEnumerable AncestorsAndSelf(this IEnumerable source) where T : JObject + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(j => j.AncestorsAndSelf()); + //} + + /// + /// Returns a collection of tokens that contains the descendants of every token in the source collection. + /// + /// The type of the objects in source, constrained to . + /// An of that contains the source collection. + /// An of that contains the descendants of every node in the source collection. + public static IJEnumerable Descendants(this IEnumerable source) where T : JContainer + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(j => j.Descendants()).AsJEnumerable(); + } + + //TODO + //public static IEnumerable DescendantsAndSelf(this IEnumerable source) where T : JContainer + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(j => j.DescendantsAndSelf()); + //} + + /// + /// Returns a collection of child properties of every object in the source collection. + /// + /// An of that contains the source collection. + /// An of that contains the properties of every object in the source collection. + public static IJEnumerable Properties(this IEnumerable source) + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(d => d.Properties()).AsJEnumerable(); + } + + /// + /// Returns a collection of child values of every object in the source collection with the given key. + /// + /// An of that contains the source collection. + /// The token key. + /// An of that contains the values of every node in the source collection with the given key. + public static IJEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key).AsJEnumerable(); + } + + /// + /// Returns a collection of child values of every object in the source collection. + /// + /// An of that contains the source collection. + /// An of that contains the values of every node in the source collection. + public static IJEnumerable Values(this IEnumerable source) + { + return source.Values(null); + } + + /// + /// Returns a collection of converted child values of every object in the source collection with the given key. + /// + /// The type to convert the values to. + /// An of that contains the source collection. + /// The token key. + /// An that contains the converted values of every node in the source collection with the given key. + public static IEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key); + } + + /// + /// Returns a collection of converted child values of every object in the source collection. + /// + /// The type to convert the values to. + /// An of that contains the source collection. + /// An that contains the converted values of every node in the source collection. + public static IEnumerable Values(this IEnumerable source) + { + return Values(source, null); + } + + /// + /// Converts the value. + /// + /// The type to convert the value to. + /// A cast as a of . + /// A converted value. + public static U Value(this IEnumerable value) + { + return value.Value(); + } + + /// + /// Converts the value. + /// + /// The source collection type. + /// The type to convert the value to. + /// A cast as a of . + /// A converted value. + public static U Value(this IEnumerable value) where T : JToken + { + ValidationUtils.ArgumentNotNull(value, "source"); + + JToken token = value as JToken; + if (token == null) + throw new ArgumentException("Source value must be a JToken."); + + return token.Convert(); + } + + + internal static IEnumerable Values(this IEnumerable source, object key) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + foreach (JToken token in source) + { + if (key == null) + { + if (token is JValue) + { + yield return Convert((JValue)token); + } + else + { + foreach (JToken t in token.Children()) + { + yield return t.Convert(); + } + } + } + else + { + JToken value = token[key]; + if (value != null) + yield return value.Convert(); + } + } + + yield break; + } + + //TODO + //public static IEnumerable InDocumentOrder(this IEnumerable source) where T : JObject; + + //public static IEnumerable Children(this IEnumerable source) where T : JToken + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(c => c.Children()); + //} + + /// + /// Returns a collection of child tokens of every array in the source collection. + /// + /// The source collection type. + /// An of that contains the source collection. + /// An of that contains the values of every node in the source collection. + public static IJEnumerable Children(this IEnumerable source) where T : JToken + { + return Children(source).AsJEnumerable(); + } + + /// + /// Returns a collection of converted child tokens of every array in the source collection. + /// + /// An of that contains the source collection. + /// The type to convert the values to. + /// The source collection type. + /// An that contains the converted values of every node in the source collection. + public static IEnumerable Children(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(c => c.Children()).Convert(); + } + + internal static IEnumerable Convert(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + foreach (T token in source) + { + yield return Convert(token); + } + } + + internal static U Convert(this T token) where T : JToken + { + if (token == null) + return default(U); + + if (token is U + // don't want to cast JValue to its interfaces, want to get the internal value + && typeof(U) != typeof(IComparable) && typeof(U) != typeof(IFormattable)) + { + // HACK + return (U)(object)token; + } + else + { + JValue value = token as JValue; + if (value == null) + throw new InvalidCastException("Cannot cast {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, token.GetType(), typeof(T))); + + if (value.Value is U) + return (U)value.Value; + + Type targetType = typeof(U); + + if (ReflectionUtils.IsNullableType(targetType)) + { + if (value.Value == null) + return default(U); + + targetType = Nullable.GetUnderlyingType(targetType); + } + + return (U)System.Convert.ChangeType(value.Value, targetType, CultureInfo.InvariantCulture); + } + } + + //TODO + //public static void Remove(this IEnumerable source) where T : JContainer; + + /// + /// Returns the input typed as . + /// + /// An of that contains the source collection. + /// The input typed as . + public static IJEnumerable AsJEnumerable(this IEnumerable source) + { + return source.AsJEnumerable(); + } + + /// + /// Returns the input typed as . + /// + /// The source collection type. + /// An of that contains the source collection. + /// The input typed as . + public static IJEnumerable AsJEnumerable(this IEnumerable source) where T : JToken + { + if (source == null) + return null; + else if (source is IJEnumerable) + return (IJEnumerable)source; + else + return new JEnumerable(source); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/IJEnumerable.cs b/Libs/JsonNet/Linq/IJEnumerable.cs new file mode 100644 index 0000000..fff8c2e --- /dev/null +++ b/Libs/JsonNet/Linq/IJEnumerable.cs @@ -0,0 +1,46 @@ +#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.Collections.Generic; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a collection of objects. + /// + /// The type of token + public interface IJEnumerable< +#if !(NET20 || NET35) + out +#endif + T> : IEnumerable where T : JToken + { + /// + /// Gets the with the specified key. + /// + /// + IJEnumerable this[object key] { get; } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JArray.cs b/Libs/JsonNet/Linq/JArray.cs new file mode 100644 index 0000000..b347eab --- /dev/null +++ b/Libs/JsonNet/Linq/JArray.cs @@ -0,0 +1,368 @@ +#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; +using System.Collections.Generic; +using Newtonsoft.Json.Utilities; +using System.IO; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON array. + /// + /// + /// + /// + public class JArray : JContainer, IList + { + private readonly List _values = new List(); + + /// + /// Gets the container's children tokens. + /// + /// The container's children tokens. + protected override IList ChildrenTokens + { + get { return _values; } + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Array; } + } + + /// + /// Initializes a new instance of the class. + /// + public JArray() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JArray(JArray other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the array. + public JArray(params object[] content) + : this((object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the array. + public JArray(object content) + { + Add(content); + } + + internal override bool DeepEquals(JToken node) + { + JArray t = node as JArray; + return (t != null && ContentsEqual(t)); + } + + internal override JToken CloneToken() + { + return new JArray(this); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public new static JArray Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading JArray from JsonReader."); + } + + while (reader.TokenType == JsonToken.Comment) + { + reader.Read(); + } + + if (reader.TokenType != JsonToken.StartArray) + throw JsonReaderException.Create(reader, "Error reading JArray from JsonReader. Current JsonReader item is not an array: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JArray a = new JArray(); + a.SetLineInfo(reader as IJsonLineInfo); + + a.ReadTokenFrom(reader); + + return a; + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + /// + /// + /// + public new static JArray Parse(string json) + { + using (JsonReader reader = new JsonTextReader(new StringReader(json))) + { + JArray a = Load(reader); + + if (reader.Read() && reader.TokenType != JsonToken.Comment) + throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content."); + + return a; + } + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the values of the specified object + public new static JArray FromObject(object o) + { + return FromObject(o, JsonSerializer.CreateDefault()); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// The that will be used to read the object. + /// A with the values of the specified object + public new static JArray FromObject(object o, JsonSerializer jsonSerializer) + { + JToken token = FromObjectInternal(o, jsonSerializer); + + if (token.Type != JTokenType.Array) + throw new ArgumentException("Object serialized to {0}. JArray instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + return (JArray)token; + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartArray(); + + for (int i = 0; i < _values.Count; i++) + { + _values[i].WriteTo(writer, converters); + } + + writer.WriteEndArray(); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Accessed JArray values with invalid key value: {0}. Array position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return GetItem((int)key); + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Set JArray values with invalid key value: {0}. Array position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + SetItem((int)key, value); + } + } + + /// + /// Gets or sets the at the specified index. + /// + /// + public JToken this[int index] + { + get { return GetItem(index); } + set { SetItem(index, value); } + } + + internal override void MergeItem(object content, JsonMergeSettings settings) + { + IEnumerable a = (IsMultiContent(content) || content is JArray) + ? (IEnumerable)content + : null; + if (a == null) + return; + + MergeEnumerableContent(this, a, settings); + } + + #region IList Members + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(JToken item) + { + return IndexOfItem(item); + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// + /// is not a valid index in the . + /// The is read-only. + public void Insert(int index, JToken item) + { + InsertItem(index, item, false); + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// + /// is not a valid index in the . + /// The is read-only. + public void RemoveAt(int index) + { + RemoveItemAt(index); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return Children().GetEnumerator(); + } + #endregion + + #region ICollection Members + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(JToken item) + { + Add((object)item); + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + ClearItems(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(JToken item) + { + return ContainsItem(item); + } + + /// + /// Copies to. + /// + /// The array. + /// Index of the array. + public void CopyTo(JToken[] array, int arrayIndex) + { + CopyItemsTo(array, arrayIndex); + } + + /// + /// Gets a value indicating whether the is read-only. + /// + /// true if the is read-only; otherwise, false. + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// The is read-only. + public bool Remove(JToken item) + { + return RemoveItem(item); + } + #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JConstructor.cs b/Libs/JsonNet/Linq/JConstructor.cs new file mode 100644 index 0000000..b267393 --- /dev/null +++ b/Libs/JsonNet/Linq/JConstructor.cs @@ -0,0 +1,218 @@ +#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; +using System.Collections.Generic; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON constructor. + /// + public class JConstructor : JContainer + { + private string _name; + private readonly List _values = new List(); + + /// + /// Gets the container's children tokens. + /// + /// The container's children tokens. + protected override IList ChildrenTokens + { + get { return _values; } + } + + internal override void MergeItem(object content, JsonMergeSettings settings) + { + JConstructor c = content as JConstructor; + if (c == null) + return; + + if (c.Name != null) + Name = c.Name; + MergeEnumerableContent(this, c, settings); + } + + /// + /// Gets or sets the name of this constructor. + /// + /// The constructor name. + public string Name + { + get { return _name; } + set { _name = value; } + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Constructor; } + } + + /// + /// Initializes a new instance of the class. + /// + public JConstructor() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JConstructor(JConstructor other) + : base(other) + { + _name = other.Name; + } + + /// + /// Initializes a new instance of the class with the specified name and content. + /// + /// The constructor name. + /// The contents of the constructor. + public JConstructor(string name, params object[] content) + : this(name, (object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified name and content. + /// + /// The constructor name. + /// The contents of the constructor. + public JConstructor(string name, object content) + : this(name) + { + Add(content); + } + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// The constructor name. + public JConstructor(string name) + { + ValidationUtils.ArgumentNotNullOrEmpty(name, "name"); + + _name = name; + } + + internal override bool DeepEquals(JToken node) + { + JConstructor c = node as JConstructor; + return (c != null && _name == c.Name && ContentsEqual(c)); + } + + internal override JToken CloneToken() + { + return new JConstructor(this); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartConstructor(_name); + + foreach (JToken token in Children()) + { + token.WriteTo(writer, converters); + } + + writer.WriteEndConstructor(); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Accessed JConstructor values with invalid key value: {0}. Argument position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return GetItem((int)key); + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Set JConstructor values with invalid key value: {0}. Argument position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + SetItem((int)key, value); + } + } + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ContentsHashCode(); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public new static JConstructor Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading JConstructor from JsonReader."); + } + + while (reader.TokenType == JsonToken.Comment) + { + reader.Read(); + } + + if (reader.TokenType != JsonToken.StartConstructor) + throw JsonReaderException.Create(reader, "Error reading JConstructor from JsonReader. Current JsonReader item is not a constructor: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JConstructor c = new JConstructor((string)reader.Value); + c.SetLineInfo(reader as IJsonLineInfo); + + c.ReadTokenFrom(reader); + + return c; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JContainer.cs b/Libs/JsonNet/Linq/JContainer.cs new file mode 100644 index 0000000..3d16778 --- /dev/null +++ b/Libs/JsonNet/Linq/JContainer.cs @@ -0,0 +1,1115 @@ +#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; +#if !PORTABLE40 +using System.Collections.Specialized; +#endif +using System.Threading; +using Newtonsoft.Json.Utilities; +using System.Collections; +using System.Globalization; +using System.ComponentModel; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a token that can contain other tokens. + /// + public abstract class JContainer : JToken, IList +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + , ITypedList, IBindingList +#elif PORTABLE + , INotifyCollectionChanged +#endif + , IList +#if !(NET20 || NET35 || NETFX_CORE || PORTABLE40 || PORTABLE) + , INotifyCollectionChanged +#endif + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + internal ListChangedEventHandler _listChanged; + internal AddingNewEventHandler _addingNew; + + /// + /// Occurs when the list changes or an item in the list changes. + /// + public event ListChangedEventHandler ListChanged + { + add { _listChanged += value; } + remove { _listChanged -= value; } + } + + /// + /// Occurs before an item is added to the collection. + /// + public event AddingNewEventHandler AddingNew + { + add { _addingNew += value; } + remove { _addingNew -= value; } + } +#endif +#if !(NET20 || NET35 || PORTABLE40) + internal NotifyCollectionChangedEventHandler _collectionChanged; + + /// + /// Occurs when the items list of the collection has changed, or the collection is reset. + /// + public event NotifyCollectionChangedEventHandler CollectionChanged + { + add { _collectionChanged += value; } + remove { _collectionChanged -= value; } + } +#endif + + /// + /// Gets the container's children tokens. + /// + /// The container's children tokens. + protected abstract IList ChildrenTokens { get; } + + private object _syncRoot; +#if !(PORTABLE40) + private bool _busy; +#endif + + internal JContainer() + { + } + + internal JContainer(JContainer other) + : this() + { + ValidationUtils.ArgumentNotNull(other, "c"); + + int i = 0; + foreach (JToken child in other) + { + AddInternal(i, child, false); + i++; + } + } + + internal void CheckReentrancy() + { +#if !(PORTABLE40) + if (_busy) + throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType())); +#endif + } + + internal virtual IList CreateChildrenCollection() + { + return new List(); + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnAddingNew(AddingNewEventArgs e) + { + AddingNewEventHandler handler = _addingNew; + if (handler != null) + handler(this, e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnListChanged(ListChangedEventArgs e) + { + ListChangedEventHandler handler = _listChanged; + + if (handler != null) + { + _busy = true; + try + { + handler(this, e); + } + finally + { + _busy = false; + } + } + } +#endif +#if !(NET20 || NET35 || PORTABLE40) + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + NotifyCollectionChangedEventHandler handler = _collectionChanged; + + if (handler != null) + { + _busy = true; + try + { + handler(this, e); + } + finally + { + _busy = false; + } + } + } +#endif + + /// + /// Gets a value indicating whether this token has child tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public override bool HasValues + { + get { return ChildrenTokens.Count > 0; } + } + + internal bool ContentsEqual(JContainer container) + { + if (container == this) + return true; + + IList t1 = ChildrenTokens; + IList t2 = container.ChildrenTokens; + + if (t1.Count != t2.Count) + return false; + + for (int i = 0; i < t1.Count; i++) + { + if (!t1[i].DeepEquals(t2[i])) + return false; + } + + return true; + } + + /// + /// Get the first child token of this token. + /// + /// + /// A containing the first child token of the . + /// + public override JToken First + { + get { return ChildrenTokens.FirstOrDefault(); } + } + + /// + /// Get the last child token of this token. + /// + /// + /// A containing the last child token of the . + /// + public override JToken Last + { + get { return ChildrenTokens.LastOrDefault(); } + } + + /// + /// Returns a collection of the child tokens of this token, in document order. + /// + /// + /// An of containing the child tokens of this , in document order. + /// + public override JEnumerable Children() + { + return new JEnumerable(ChildrenTokens); + } + + /// + /// Returns a collection of the child values of this token, in document order. + /// + /// The type to convert the values to. + /// + /// A containing the child values of this , in document order. + /// + public override IEnumerable Values() + { + return ChildrenTokens.Convert(); + } + + /// + /// Returns a collection of the descendant tokens for this token in document order. + /// + /// An containing the descendant tokens of the . + public IEnumerable Descendants() + { + foreach (JToken o in ChildrenTokens) + { + yield return o; + JContainer c = o as JContainer; + if (c != null) + { + foreach (JToken d in c.Descendants()) + { + yield return d; + } + } + } + } + + internal bool IsMultiContent(object content) + { + return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[])); + } + + internal JToken EnsureParentToken(JToken item, bool skipParentCheck) + { + if (item == null) + return JValue.CreateNull(); + + if (skipParentCheck) + return item; + + // to avoid a token having multiple parents or creating a recursive loop, create a copy if... + // the item already has a parent + // the item is being added to itself + // the item is being added to the root parent of itself + if (item.Parent != null || item == this || (item.HasValues && Root == item)) + item = item.CloneToken(); + + return item; + } + + private class JTokenReferenceEqualityComparer : IEqualityComparer + { + public static readonly JTokenReferenceEqualityComparer Instance = new JTokenReferenceEqualityComparer(); + + public bool Equals(JToken x, JToken y) + { + return ReferenceEquals(x, y); + } + + public int GetHashCode(JToken obj) + { + if (obj == null) + return 0; + + return obj.GetHashCode(); + } + } + + internal int IndexOfItem(JToken item) + { + return ChildrenTokens.IndexOf(item, JTokenReferenceEqualityComparer.Instance); + } + + internal virtual void InsertItem(int index, JToken item, bool skipParentCheck) + { + if (index > ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index must be within the bounds of the List."); + + CheckReentrancy(); + + item = EnsureParentToken(item, skipParentCheck); + + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + // haven't inserted new token yet so next token is still at the inserting index + JToken next = (index == ChildrenTokens.Count) ? null : ChildrenTokens[index]; + + ValidateToken(item, null); + + item.Parent = this; + + item.Previous = previous; + if (previous != null) + previous.Next = item; + + item.Next = next; + if (next != null) + next.Previous = item; + + ChildrenTokens.Insert(index, item); + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (_listChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index)); +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_collectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); +#endif + } + + internal virtual void RemoveItemAt(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "Index is less than 0."); + if (index >= ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index is equal to or greater than Count."); + + CheckReentrancy(); + + JToken item = ChildrenTokens[index]; + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + JToken next = (index == ChildrenTokens.Count - 1) ? null : ChildrenTokens[index + 1]; + + if (previous != null) + previous.Next = next; + if (next != null) + next.Previous = previous; + + item.Parent = null; + item.Previous = null; + item.Next = null; + + ChildrenTokens.RemoveAt(index); + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (_listChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_collectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index)); +#endif + } + + internal virtual bool RemoveItem(JToken item) + { + int index = IndexOfItem(item); + if (index >= 0) + { + RemoveItemAt(index); + return true; + } + + return false; + } + + internal virtual JToken GetItem(int index) + { + return ChildrenTokens[index]; + } + + internal virtual void SetItem(int index, JToken item) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "Index is less than 0."); + if (index >= ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index is equal to or greater than Count."); + + JToken existing = ChildrenTokens[index]; + + if (IsTokenUnchanged(existing, item)) + return; + + CheckReentrancy(); + + item = EnsureParentToken(item, false); + + ValidateToken(item, existing); + + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + JToken next = (index == ChildrenTokens.Count - 1) ? null : ChildrenTokens[index + 1]; + + item.Parent = this; + + item.Previous = previous; + if (previous != null) + previous.Next = item; + + item.Next = next; + if (next != null) + next.Previous = item; + + ChildrenTokens[index] = item; + + existing.Parent = null; + existing.Previous = null; + existing.Next = null; + +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + if (_listChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_collectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, existing, index)); +#endif + } + + internal virtual void ClearItems() + { + CheckReentrancy(); + + foreach (JToken item in ChildrenTokens) + { + item.Parent = null; + item.Previous = null; + item.Next = null; + } + + ChildrenTokens.Clear(); + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (_listChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_collectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); +#endif + } + + internal virtual void ReplaceItem(JToken existing, JToken replacement) + { + if (existing == null || existing.Parent != this) + return; + + int index = IndexOfItem(existing); + SetItem(index, replacement); + } + + internal virtual bool ContainsItem(JToken item) + { + return (IndexOfItem(item) != -1); + } + + internal virtual void CopyItemsTo(Array array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length && arrayIndex != 0) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (Count > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + + int index = 0; + foreach (JToken token in ChildrenTokens) + { + array.SetValue(token, arrayIndex + index); + index++; + } + } + + internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue) + { + JValue v1 = currentValue as JValue; + if (v1 != null) + { + // null will get turned into a JValue of type null + if (v1.Type == JTokenType.Null && newValue == null) + return true; + + return v1.Equals(newValue); + } + + return false; + } + + internal virtual void ValidateToken(JToken o, JToken existing) + { + ValidationUtils.ArgumentNotNull(o, "o"); + + if (o.Type == JTokenType.Property) + throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + } + + /// + /// Adds the specified content as children of this . + /// + /// The content to be added. + public virtual void Add(object content) + { + AddInternal(ChildrenTokens.Count, content, false); + } + + internal void AddAndSkipParentCheck(JToken token) + { + AddInternal(ChildrenTokens.Count, token, true); + } + + /// + /// Adds the specified content as the first children of this . + /// + /// The content to be added. + public void AddFirst(object content) + { + AddInternal(0, content, false); + } + + internal void AddInternal(int index, object content, bool skipParentCheck) + { + if (IsMultiContent(content)) + { + IEnumerable enumerable = (IEnumerable)content; + + int multiIndex = index; + foreach (object c in enumerable) + { + AddInternal(multiIndex, c, skipParentCheck); + multiIndex++; + } + } + else + { + JToken item = CreateFromContent(content); + + InsertItem(index, item, skipParentCheck); + } + } + + internal static JToken CreateFromContent(object content) + { + if (content is JToken) + return (JToken)content; + + return new JValue(content); + } + + /// + /// Creates an that can be used to add tokens to the . + /// + /// An that is ready to have content written to it. + public JsonWriter CreateWriter() + { + return new JTokenWriter(this); + } + + /// + /// Replaces the children nodes of this token with the specified content. + /// + /// The content. + public void ReplaceAll(object content) + { + ClearItems(); + Add(content); + } + + /// + /// Removes the child nodes from this token. + /// + public void RemoveAll() + { + ClearItems(); + } + + internal abstract void MergeItem(object content, JsonMergeSettings settings); + + /// + /// Merge the specified content into this . + /// + /// The content to be merged. + public void Merge(object content) + { + MergeItem(content, new JsonMergeSettings()); + } + + /// + /// Merge the specified content into this using . + /// + /// The content to be merged. + /// The used to merge the content. + public void Merge(object content, JsonMergeSettings settings) + { + MergeItem(content, settings); + } + + internal void ReadTokenFrom(JsonReader reader) + { + int startDepth = reader.Depth; + + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name)); + + ReadContentFrom(reader); + + int endDepth = reader.Depth; + + if (endDepth > startDepth) + throw JsonReaderException.Create(reader, "Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name)); + } + + internal void ReadContentFrom(JsonReader r) + { + ValidationUtils.ArgumentNotNull(r, "r"); + IJsonLineInfo lineInfo = r as IJsonLineInfo; + + JContainer parent = this; + + do + { + if (parent is JProperty && ((JProperty)parent).Value != null) + { + if (parent == this) + return; + + parent = parent.Parent; + } + + switch (r.TokenType) + { + case JsonToken.None: + // new reader. move to actual content + break; + case JsonToken.StartArray: + JArray a = new JArray(); + a.SetLineInfo(lineInfo); + parent.Add(a); + parent = a; + break; + + case JsonToken.EndArray: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.StartObject: + JObject o = new JObject(); + o.SetLineInfo(lineInfo); + parent.Add(o); + parent = o; + break; + case JsonToken.EndObject: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.StartConstructor: + JConstructor constructor = new JConstructor(r.Value.ToString()); + constructor.SetLineInfo(lineInfo); + parent.Add(constructor); + parent = constructor; + break; + case JsonToken.EndConstructor: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.String: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Date: + case JsonToken.Boolean: + case JsonToken.Bytes: + JValue v = new JValue(r.Value); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Comment: + v = JValue.CreateComment(r.Value.ToString()); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Null: + v = JValue.CreateNull(); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Undefined: + v = JValue.CreateUndefined(); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.PropertyName: + string propertyName = r.Value.ToString(); + JProperty property = new JProperty(propertyName); + property.SetLineInfo(lineInfo); + JObject parentObject = (JObject)parent; + // handle multiple properties with the same name in JSON + JProperty existingPropertyWithName = parentObject.Property(propertyName); + if (existingPropertyWithName == null) + parent.Add(property); + else + existingPropertyWithName.Replace(property); + parent = property; + break; + default: + throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType)); + } + } while (r.Read()); + } + + internal int ContentsHashCode() + { + int hashCode = 0; + foreach (JToken item in ChildrenTokens) + { + hashCode ^= item.GetDeepHashCode(); + } + return hashCode; + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + string ITypedList.GetListName(PropertyDescriptor[] listAccessors) + { + return string.Empty; + } + + PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) + { + ICustomTypeDescriptor d = First as ICustomTypeDescriptor; + if (d != null) + return d.GetProperties(); + + return null; + } +#endif + + #region IList Members + int IList.IndexOf(JToken item) + { + return IndexOfItem(item); + } + + void IList.Insert(int index, JToken item) + { + InsertItem(index, item, false); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + JToken IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, value); } + } + #endregion + + #region ICollection Members + void ICollection.Add(JToken item) + { + Add(item); + } + + void ICollection.Clear() + { + ClearItems(); + } + + bool ICollection.Contains(JToken item) + { + return ContainsItem(item); + } + + void ICollection.CopyTo(JToken[] array, int arrayIndex) + { + CopyItemsTo(array, arrayIndex); + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + bool ICollection.Remove(JToken item) + { + return RemoveItem(item); + } + #endregion + + private JToken EnsureValue(object value) + { + if (value == null) + return null; + + if (value is JToken) + return (JToken)value; + + throw new ArgumentException("Argument is not a JToken."); + } + + #region IList Members + int IList.Add(object value) + { + Add(EnsureValue(value)); + return Count - 1; + } + + void IList.Clear() + { + ClearItems(); + } + + bool IList.Contains(object value) + { + return ContainsItem(EnsureValue(value)); + } + + int IList.IndexOf(object value) + { + return IndexOfItem(EnsureValue(value)); + } + + void IList.Insert(int index, object value) + { + InsertItem(index, EnsureValue(value), false); + } + + bool IList.IsFixedSize + { + get { return false; } + } + + bool IList.IsReadOnly + { + get { return false; } + } + + void IList.Remove(object value) + { + RemoveItem(EnsureValue(value)); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + object IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, EnsureValue(value)); } + } + #endregion + + #region ICollection Members + void ICollection.CopyTo(Array array, int index) + { + CopyItemsTo(array, index); + } + + /// + /// Gets the count of child JSON tokens. + /// + /// The count of child JSON tokens + public int Count + { + get { return ChildrenTokens.Count; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + #endregion + + #region IBindingList Members +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + void IBindingList.AddIndex(PropertyDescriptor property) + { + } + + object IBindingList.AddNew() + { + AddingNewEventArgs args = new AddingNewEventArgs(); + OnAddingNew(args); + + if (args.NewObject == null) + throw new JsonException("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType())); + + if (!(args.NewObject is JToken)) + throw new JsonException("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JToken))); + + JToken newItem = (JToken)args.NewObject; + Add(newItem); + + return newItem; + } + + bool IBindingList.AllowEdit + { + get { return true; } + } + + bool IBindingList.AllowNew + { + get { return true; } + } + + bool IBindingList.AllowRemove + { + get { return true; } + } + + void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) + { + throw new NotSupportedException(); + } + + int IBindingList.Find(PropertyDescriptor property, object key) + { + throw new NotSupportedException(); + } + + bool IBindingList.IsSorted + { + get { return false; } + } + + void IBindingList.RemoveIndex(PropertyDescriptor property) + { + } + + void IBindingList.RemoveSort() + { + throw new NotSupportedException(); + } + + ListSortDirection IBindingList.SortDirection + { + get { return ListSortDirection.Ascending; } + } + + PropertyDescriptor IBindingList.SortProperty + { + get { return null; } + } + + bool IBindingList.SupportsChangeNotification + { + get { return true; } + } + + bool IBindingList.SupportsSearching + { + get { return false; } + } + + bool IBindingList.SupportsSorting + { + get { return false; } + } +#endif + #endregion + + internal static void MergeEnumerableContent(JContainer target, IEnumerable content, JsonMergeSettings settings) + { + switch (settings.MergeArrayHandling) + { + case MergeArrayHandling.Concat: + foreach (JToken item in content) + { + target.Add(item); + } + break; + case MergeArrayHandling.Union: +#if !NET20 + HashSet items = new HashSet(target, EqualityComparer); + + foreach (JToken item in content) + { + if (items.Add(item)) + { + target.Add(item); + } + } +#else + IDictionary items = new Dictionary(EqualityComparer); + foreach (JToken t in target) + { + items[t] = true; + } + + foreach (JToken item in content) + { + if (!items.ContainsKey(item)) + { + items[item] = true; + target.Add(item); + } + } +#endif + break; + case MergeArrayHandling.Replace: + target.ClearItems(); + foreach (JToken item in content) + { + target.Add(item); + } + break; + case MergeArrayHandling.Merge: + int i = 0; + foreach (object targetItem in content) + { + if (i < target.Count) + { + JToken sourceItem = target[i]; + + JContainer existingContainer = sourceItem as JContainer; + if (existingContainer != null) + { + existingContainer.Merge(targetItem, settings); + } + else + { + if (targetItem != null) + { + JToken contentValue = CreateFromContent(targetItem); + if (contentValue.Type != JTokenType.Null) + target[i] = contentValue; + } + } + } + else + { + target.Add(targetItem); + } + + i++; + } + break; + default: + throw new ArgumentOutOfRangeException("settings", "Unexpected merge array handling when merging JSON."); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JEnumerable.cs b/Libs/JsonNet/Linq/JEnumerable.cs new file mode 100644 index 0000000..5cf4fc8 --- /dev/null +++ b/Libs/JsonNet/Linq/JEnumerable.cs @@ -0,0 +1,143 @@ +#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; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a collection of objects. + /// + /// The type of token + public struct JEnumerable : IJEnumerable, IEquatable> where T : JToken + { + /// + /// An empty collection of objects. + /// + public static readonly JEnumerable Empty = new JEnumerable(Enumerable.Empty()); + + private readonly IEnumerable _enumerable; + + /// + /// Initializes a new instance of the struct. + /// + /// The enumerable. + public JEnumerable(IEnumerable enumerable) + { + ValidationUtils.ArgumentNotNull(enumerable, "enumerable"); + + _enumerable = enumerable; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + if (_enumerable == null) + return Empty.GetEnumerator(); + + return _enumerable.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Gets the with the specified key. + /// + /// + public IJEnumerable this[object key] + { + get + { + if (_enumerable == null) + return JEnumerable.Empty; + + return new JEnumerable(_enumerable.Values(key)); + } + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public bool Equals(JEnumerable other) + { + return Equals(_enumerable, other._enumerable); + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (obj is JEnumerable) + return Equals((JEnumerable)obj); + + return false; + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + if (_enumerable == null) + return 0; + + return _enumerable.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JObject.cs b/Libs/JsonNet/Linq/JObject.cs new file mode 100644 index 0000000..0ef4b0e --- /dev/null +++ b/Libs/JsonNet/Linq/JObject.cs @@ -0,0 +1,824 @@ +#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.Collections.ObjectModel; +#if !PORTABLE40 +using System.Collections.Specialized; +#endif +using System.ComponentModel; +#if !(NET35 || NET20 || PORTABLE40) +using System.Dynamic; +using System.Linq.Expressions; +#endif +using System.IO; +using Newtonsoft.Json.Utilities; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON object. + /// + /// + /// + /// + public class JObject : JContainer, IDictionary, INotifyPropertyChanged +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + , ICustomTypeDescriptor +#endif +#if !(NET20 || NETFX_CORE || PORTABLE40 || PORTABLE) + , INotifyPropertyChanging +#endif + { + private readonly JPropertyKeyedCollection _properties = new JPropertyKeyedCollection(); + + /// + /// Gets the container's children tokens. + /// + /// The container's children tokens. + protected override IList ChildrenTokens + { + get { return _properties; } + } + + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + +#if !(NET20 || NETFX_CORE || PORTABLE || PORTABLE40) + /// + /// Occurs when a property value is changing. + /// + public event PropertyChangingEventHandler PropertyChanging; +#endif + + /// + /// Initializes a new instance of the class. + /// + public JObject() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JObject(JObject other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the object. + public JObject(params object[] content) + : this((object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the object. + public JObject(object content) + { + Add(content); + } + + internal override bool DeepEquals(JToken node) + { + JObject t = node as JObject; + if (t == null) + return false; + + return _properties.Compare(t._properties); + } + + internal override void InsertItem(int index, JToken item, bool skipParentCheck) + { + // don't add comments to JObject, no name to reference comment by + if (item != null && item.Type == JTokenType.Comment) + return; + + base.InsertItem(index, item, skipParentCheck); + } + + internal override void ValidateToken(JToken o, JToken existing) + { + ValidationUtils.ArgumentNotNull(o, "o"); + + if (o.Type != JTokenType.Property) + throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + + JProperty newProperty = (JProperty)o; + + if (existing != null) + { + JProperty existingProperty = (JProperty)existing; + + if (newProperty.Name == existingProperty.Name) + return; + } + + if (_properties.TryGetValue(newProperty.Name, out existing)) + throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, newProperty.Name, GetType())); + } + + internal override void MergeItem(object content, JsonMergeSettings settings) + { + JObject o = content as JObject; + if (o == null) + return; + + foreach (KeyValuePair contentItem in o) + { + JProperty existingProperty = Property(contentItem.Key); + + if (existingProperty == null) + { + Add(contentItem.Key, contentItem.Value); + } + else if (contentItem.Value != null) + { + JContainer existingContainer = existingProperty.Value as JContainer; + if (existingContainer == null) + { + if (contentItem.Value.Type != JTokenType.Null) + existingProperty.Value = contentItem.Value; + } + else if (existingContainer.Type != contentItem.Value.Type) + { + existingProperty.Value = contentItem.Value; + } + else + { + existingContainer.Merge(contentItem.Value, settings); + } + } + } + } + + internal void InternalPropertyChanged(JProperty childProperty) + { + OnPropertyChanged(childProperty.Name); +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (_listChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOfItem(childProperty))); +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_collectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, childProperty, childProperty, IndexOfItem(childProperty))); +#endif + } + + internal void InternalPropertyChanging(JProperty childProperty) + { +#if !(NET20 || NETFX_CORE || PORTABLE40 || PORTABLE) + OnPropertyChanging(childProperty.Name); +#endif + } + + internal override JToken CloneToken() + { + return new JObject(this); + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Object; } + } + + /// + /// Gets an of this object's properties. + /// + /// An of this object's properties. + public IEnumerable Properties() + { + return _properties.Cast(); + } + + /// + /// Gets a the specified name. + /// + /// The property name. + /// A with the specified name or null. + public JProperty Property(string name) + { + if (name == null) + return null; + + JToken property; + _properties.TryGetValue(name, out property); + return (JProperty)property; + } + + /// + /// Gets an of this object's property values. + /// + /// An of this object's property values. + public JEnumerable PropertyValues() + { + return new JEnumerable(Properties().Select(p => p.Value)); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + string propertyName = key as string; + if (propertyName == null) + throw new ArgumentException("Accessed JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return this[propertyName]; + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + string propertyName = key as string; + if (propertyName == null) + throw new ArgumentException("Set JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + this[propertyName] = value; + } + } + + /// + /// Gets or sets the with the specified property name. + /// + /// + public JToken this[string propertyName] + { + get + { + ValidationUtils.ArgumentNotNull(propertyName, "propertyName"); + + JProperty property = Property(propertyName); + + return (property != null) ? property.Value : null; + } + set + { + JProperty property = Property(propertyName); + if (property != null) + { + property.Value = value; + } + else + { +#if !(NET20 || NETFX_CORE || PORTABLE40 || PORTABLE) + OnPropertyChanging(propertyName); +#endif + Add(new JProperty(propertyName, value)); + OnPropertyChanged(propertyName); + } + } + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public new static JObject Load(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading JObject from JsonReader."); + } + + while (reader.TokenType == JsonToken.Comment) + { + reader.Read(); + } + + if (reader.TokenType != JsonToken.StartObject) + { + throw JsonReaderException.Create(reader, "Error reading JObject from JsonReader. Current JsonReader item is not an object: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + JObject o = new JObject(); + o.SetLineInfo(reader as IJsonLineInfo); + + o.ReadTokenFrom(reader); + + return o; + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + /// + /// + /// + public new static JObject Parse(string json) + { + using (JsonReader reader = new JsonTextReader(new StringReader(json))) + { + JObject o = Load(reader); + + if (reader.Read() && reader.TokenType != JsonToken.Comment) + throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content."); + + return o; + } + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the values of the specified object + public new static JObject FromObject(object o) + { + return FromObject(o, JsonSerializer.CreateDefault()); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// The that will be used to read the object. + /// A with the values of the specified object + public new static JObject FromObject(object o, JsonSerializer jsonSerializer) + { + JToken token = FromObjectInternal(o, jsonSerializer); + + if (token != null && token.Type != JTokenType.Object) + throw new ArgumentException("Object serialized to {0}. JObject instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + return (JObject)token; + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartObject(); + + for (int i = 0; i < _properties.Count; i++) + { + _properties[i].WriteTo(writer, converters); + } + + writer.WriteEndObject(); + } + + /// + /// Gets the with the specified property name. + /// + /// Name of the property. + /// The with the specified property name. + public JToken GetValue(string propertyName) + { + return GetValue(propertyName, StringComparison.Ordinal); + } + + /// + /// Gets the with the specified property name. + /// The exact property name will be searched for first and if no matching property is found then + /// the will be used to match a property. + /// + /// Name of the property. + /// One of the enumeration values that specifies how the strings will be compared. + /// The with the specified property name. + public JToken GetValue(string propertyName, StringComparison comparison) + { + if (propertyName == null) + return null; + + // attempt to get value via dictionary first for performance + JProperty property = Property(propertyName); + if (property != null) + return property.Value; + + // test above already uses this comparison so no need to repeat + if (comparison != StringComparison.Ordinal) + { + foreach (JProperty p in _properties) + { + if (string.Equals(p.Name, propertyName, comparison)) + return p.Value; + } + } + + return null; + } + + /// + /// Tries to get the with the specified property name. + /// The exact property name will be searched for first and if no matching property is found then + /// the will be used to match a property. + /// + /// Name of the property. + /// The value. + /// One of the enumeration values that specifies how the strings will be compared. + /// true if a value was successfully retrieved; otherwise, false. + public bool TryGetValue(string propertyName, StringComparison comparison, out JToken value) + { + value = GetValue(propertyName, comparison); + return (value != null); + } + + #region IDictionary Members + /// + /// Adds the specified property name. + /// + /// Name of the property. + /// The value. + public void Add(string propertyName, JToken value) + { + Add(new JProperty(propertyName, value)); + } + + bool IDictionary.ContainsKey(string key) + { + return _properties.Contains(key); + } + + ICollection IDictionary.Keys + { + // todo: make order the collection returned match JObject order + get { return _properties.Keys; } + } + + /// + /// Removes the property with the specified name. + /// + /// Name of the property. + /// true if item was successfully removed; otherwise, false. + public bool Remove(string propertyName) + { + JProperty property = Property(propertyName); + if (property == null) + return false; + + property.Remove(); + return true; + } + + /// + /// Tries the get value. + /// + /// Name of the property. + /// The value. + /// true if a value was successfully retrieved; otherwise, false. + public bool TryGetValue(string propertyName, out JToken value) + { + JProperty property = Property(propertyName); + if (property == null) + { + value = null; + return false; + } + + value = property.Value; + return true; + } + + ICollection IDictionary.Values + { + get + { + // todo: need to wrap _properties.Values with a collection to get the JProperty value + throw new NotImplementedException(); + } + } + #endregion + + #region ICollection> Members + void ICollection>.Add(KeyValuePair item) + { + Add(new JProperty(item.Key, item.Value)); + } + + void ICollection>.Clear() + { + RemoveAll(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + JProperty property = Property(item.Key); + if (property == null) + return false; + + return (property.Value == item.Value); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length && arrayIndex != 0) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (Count > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + + int index = 0; + foreach (JProperty property in _properties) + { + array[arrayIndex + index] = new KeyValuePair(property.Name, property.Value); + index++; + } + } + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + bool ICollection>.Remove(KeyValuePair item) + { + if (!((ICollection>)this).Contains(item)) + return false; + + ((IDictionary)this).Remove(item.Key); + return true; + } + #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + foreach (JProperty property in _properties) + { + yield return new KeyValuePair(property.Name, property.Value); + } + } + + /// + /// Raises the event with the provided arguments. + /// + /// Name of the property. + protected virtual void OnPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE || NET20) + /// + /// Raises the event with the provided arguments. + /// + /// Name of the property. + protected virtual void OnPropertyChanging(string propertyName) + { + if (PropertyChanging != null) + PropertyChanging(this, new PropertyChangingEventArgs(propertyName)); + } +#endif + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // include custom type descriptor on JObject rather than use a provider because the properties are specific to a type + + #region ICustomTypeDescriptor + /// + /// Returns the properties for this instance of a component. + /// + /// + /// A that represents the properties for this component instance. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() + { + return ((ICustomTypeDescriptor)this).GetProperties(null); + } + + /// + /// Returns the properties for this instance of a component using the attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// A that represents the filtered properties for this component instance. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + { + PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null); + + foreach (KeyValuePair propertyValue in this) + { + descriptors.Add(new JPropertyDescriptor(propertyValue.Key)); + } + + return descriptors; + } + + /// + /// Returns a collection of custom attributes for this instance of a component. + /// + /// + /// An containing the attributes for this object. + /// + AttributeCollection ICustomTypeDescriptor.GetAttributes() + { + return AttributeCollection.Empty; + } + + /// + /// Returns the class name of this instance of a component. + /// + /// + /// The class name of the object, or null if the class does not have a name. + /// + string ICustomTypeDescriptor.GetClassName() + { + return null; + } + + /// + /// Returns the name of this instance of a component. + /// + /// + /// The name of the object, or null if the object does not have a name. + /// + string ICustomTypeDescriptor.GetComponentName() + { + return null; + } + + /// + /// Returns a type converter for this instance of a component. + /// + /// + /// A that is the converter for this object, or null if there is no for this object. + /// + TypeConverter ICustomTypeDescriptor.GetConverter() + { + return new TypeConverter(); + } + + /// + /// Returns the default event for this instance of a component. + /// + /// + /// An that represents the default event for this object, or null if this object does not have events. + /// + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + { + return null; + } + + /// + /// Returns the default property for this instance of a component. + /// + /// + /// A that represents the default property for this object, or null if this object does not have properties. + /// + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + { + return null; + } + + /// + /// Returns an editor of the specified type for this instance of a component. + /// + /// A that represents the editor for this object. + /// + /// An of the specified type that is the editor for this object, or null if the editor cannot be found. + /// + object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + { + return null; + } + + /// + /// Returns the events for this instance of a component using the specified attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// An that represents the filtered events for this component instance. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns the events for this instance of a component. + /// + /// + /// An that represents the events for this component instance. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns an object that contains the property described by the specified property descriptor. + /// + /// A that represents the property whose owner is to be found. + /// + /// An that represents the owner of the specified property. + /// + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + { + return null; + } + #endregion + +#endif + +#if !(NET35 || NET20 || PORTABLE40) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new JObjectDynamicProxy(), true); + } + + private class JObjectDynamicProxy : DynamicProxy + { + public override bool TryGetMember(JObject instance, GetMemberBinder binder, out object result) + { + // result can be null + result = instance[binder.Name]; + return true; + } + + public override bool TrySetMember(JObject instance, SetMemberBinder binder, object value) + { + JToken v = value as JToken; + + // this can throw an error if value isn't a valid for a JValue + if (v == null) + v = new JValue(value); + + instance[binder.Name] = v; + return true; + } + + public override IEnumerable GetDynamicMemberNames(JObject instance) + { + return instance.Properties().Select(p => p.Name); + } + } +#endif + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JProperty.cs b/Libs/JsonNet/Linq/JProperty.cs new file mode 100644 index 0000000..ab27760 --- /dev/null +++ b/Libs/JsonNet/Linq/JProperty.cs @@ -0,0 +1,355 @@ +#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; +using System.Collections.Generic; +using Newtonsoft.Json.Utilities; +using System.Diagnostics; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON property. + /// + public class JProperty : JContainer + { + #region JPropertyList + private class JPropertyList : IList + { + internal JToken _token; + + public IEnumerator GetEnumerator() + { + if (_token != null) + yield return _token; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(JToken item) + { + _token = item; + } + + public void Clear() + { + _token = null; + } + + public bool Contains(JToken item) + { + return (_token == item); + } + + public void CopyTo(JToken[] array, int arrayIndex) + { + if (_token != null) + array[arrayIndex] = _token; + } + + public bool Remove(JToken item) + { + if (_token == item) + { + _token = null; + return true; + } + return false; + } + + public int Count + { + get { return (_token != null) ? 1 : 0; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public int IndexOf(JToken item) + { + return (_token == item) ? 0 : -1; + } + + public void Insert(int index, JToken item) + { + if (index == 0) + _token = item; + } + + public void RemoveAt(int index) + { + if (index == 0) + _token = null; + } + + public JToken this[int index] + { + get { return (index == 0) ? _token : null; } + set + { + if (index == 0) + _token = value; + } + } + } + #endregion + + private readonly JPropertyList _content = new JPropertyList(); + private readonly string _name; + + /// + /// Gets the container's children tokens. + /// + /// The container's children tokens. + protected override IList ChildrenTokens + { + get { return _content; } + } + + /// + /// Gets the property name. + /// + /// The property name. + public string Name + { + [DebuggerStepThrough] + get { return _name; } + } + + /// + /// Gets or sets the property value. + /// + /// The property value. + public JToken Value + { + [DebuggerStepThrough] + get { return _content._token; } + set + { + CheckReentrancy(); + + JToken newValue = value ?? JValue.CreateNull(); + + if (_content._token == null) + { + InsertItem(0, newValue, false); + } + else + { + SetItem(0, newValue); + } + } + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JProperty(JProperty other) + : base(other) + { + _name = other.Name; + } + + internal override JToken GetItem(int index) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + return Value; + } + + internal override void SetItem(int index, JToken item) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + if (IsTokenUnchanged(Value, item)) + return; + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanging(this); + + base.SetItem(0, item); + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanged(this); + } + + internal override bool RemoveItem(JToken item) + { + throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void RemoveItemAt(int index) + { + throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void InsertItem(int index, JToken item, bool skipParentCheck) + { + // don't add comments to JProperty + if (item != null && item.Type == JTokenType.Comment) + return; + + if (Value != null) + throw new JsonException("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + + base.InsertItem(0, item, false); + } + + internal override bool ContainsItem(JToken item) + { + return (Value == item); + } + + internal override void MergeItem(object content, JsonMergeSettings settings) + { + JProperty p = content as JProperty; + if (p == null) + return; + + if (p.Value != null && p.Value.Type != JTokenType.Null) + Value = p.Value; + } + + internal override void ClearItems() + { + throw new JsonException("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override bool DeepEquals(JToken node) + { + JProperty t = node as JProperty; + return (t != null && _name == t.Name && ContentsEqual(t)); + } + + internal override JToken CloneToken() + { + return new JProperty(this); + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + [DebuggerStepThrough] + get { return JTokenType.Property; } + } + + internal JProperty(string name) + { + // called from JTokenWriter + ValidationUtils.ArgumentNotNull(name, "name"); + + _name = name; + } + + /// + /// Initializes a new instance of the class. + /// + /// The property name. + /// The property content. + public JProperty(string name, params object[] content) + : this(name, (object)content) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The property name. + /// The property content. + public JProperty(string name, object content) + { + ValidationUtils.ArgumentNotNull(name, "name"); + + _name = name; + + Value = IsMultiContent(content) + ? new JArray(content) + : CreateFromContent(content); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WritePropertyName(_name); + + JToken value = Value; + if (value != null) + value.WriteTo(writer, converters); + else + writer.WriteNull(); + } + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ((Value != null) ? Value.GetDeepHashCode() : 0); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public new static JProperty Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading JProperty from JsonReader."); + } + + while (reader.TokenType == JsonToken.Comment) + { + reader.Read(); + } + + if (reader.TokenType != JsonToken.PropertyName) + throw JsonReaderException.Create(reader, "Error reading JProperty from JsonReader. Current JsonReader item is not a property: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JProperty p = new JProperty((string)reader.Value); + p.SetLineInfo(reader as IJsonLineInfo); + + p.ReadTokenFrom(reader); + + return p; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JPropertyDescriptor.cs b/Libs/JsonNet/Linq/JPropertyDescriptor.cs new file mode 100644 index 0000000..71360a8 --- /dev/null +++ b/Libs/JsonNet/Linq/JPropertyDescriptor.cs @@ -0,0 +1,166 @@ +#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 !(NETFX_CORE || PORTABLE || PORTABLE40) +using System; +using System.ComponentModel; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a view of a . + /// + public class JPropertyDescriptor : PropertyDescriptor + { + /// + /// Initializes a new instance of the class. + /// + /// The name. + public JPropertyDescriptor(string name) + : base(name, null) + { + } + + private static JObject CastInstance(object instance) + { + return (JObject)instance; + } + + /// + /// When overridden in a derived class, returns whether resetting an object changes its value. + /// + /// + /// true if resetting the component changes its value; otherwise, false. + /// + /// The component to test for reset capability. + /// + public override bool CanResetValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the current value of the property on a component. + /// + /// + /// The value of a property for a given component. + /// + /// The component with the property for which to retrieve the value. + /// + public override object GetValue(object component) + { + JToken token = CastInstance(component)[Name]; + + return token; + } + + /// + /// When overridden in a derived class, resets the value for this property of the component to the default value. + /// + /// The component with the property value that is to be reset to the default value. + /// + public override void ResetValue(object component) + { + } + + /// + /// When overridden in a derived class, sets the value of the component to a different value. + /// + /// The component with the property value that is to be set. + /// The new value. + /// + public override void SetValue(object component, object value) + { + JToken token = (value is JToken) ? (JToken)value : new JValue(value); + + CastInstance(component)[Name] = token; + } + + /// + /// When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. + /// + /// + /// true if the property should be persisted; otherwise, false. + /// + /// The component with the property to be examined for persistence. + /// + public override bool ShouldSerializeValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the type of the component this property is bound to. + /// + /// + /// A that represents the type of component this property is bound to. When the or methods are invoked, the object specified might be an instance of this type. + /// + public override Type ComponentType + { + get { return typeof(JObject); } + } + + /// + /// When overridden in a derived class, gets a value indicating whether this property is read-only. + /// + /// + /// true if the property is read-only; otherwise, false. + /// + public override bool IsReadOnly + { + get { return false; } + } + + /// + /// When overridden in a derived class, gets the type of the property. + /// + /// + /// A that represents the type of the property. + /// + public override Type PropertyType + { + get { return typeof(object); } + } + + /// + /// Gets the hash code for the name of the member. + /// + /// + /// + /// The hash code for the name of the member. + /// + protected override int NameHashCode + { + get + { + // override property to fix up an error in its documentation + int nameHashCode = base.NameHashCode; + return nameHashCode; + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JPropertyKeyedCollection.cs b/Libs/JsonNet/Linq/JPropertyKeyedCollection.cs new file mode 100644 index 0000000..205f4c8 --- /dev/null +++ b/Libs/JsonNet/Linq/JPropertyKeyedCollection.cs @@ -0,0 +1,234 @@ +#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.Collections.ObjectModel; + +namespace Newtonsoft.Json.Linq +{ + internal class JPropertyKeyedCollection : Collection + { + private static readonly IEqualityComparer Comparer = StringComparer.Ordinal; + + private Dictionary _dictionary; + + private void AddKey(string key, JToken item) + { + EnsureDictionary(); + _dictionary[key] = item; + } + + protected void ChangeItemKey(JToken item, string newKey) + { + if (!ContainsItem(item)) + throw new ArgumentException("The specified item does not exist in this KeyedCollection."); + + string keyForItem = GetKeyForItem(item); + if (!Comparer.Equals(keyForItem, newKey)) + { + if (newKey != null) + AddKey(newKey, item); + + if (keyForItem != null) + RemoveKey(keyForItem); + } + } + + protected override void ClearItems() + { + base.ClearItems(); + + if (_dictionary != null) + _dictionary.Clear(); + } + + public bool Contains(string key) + { + if (key == null) + throw new ArgumentNullException("key"); + + if (_dictionary != null) + return _dictionary.ContainsKey(key); + + return false; + } + + private bool ContainsItem(JToken item) + { + if (_dictionary == null) + return false; + + string key = GetKeyForItem(item); + JToken value; + return _dictionary.TryGetValue(key, out value); + } + + private void EnsureDictionary() + { + if (_dictionary == null) + _dictionary = new Dictionary(Comparer); + } + + private string GetKeyForItem(JToken item) + { + return ((JProperty)item).Name; + } + + protected override void InsertItem(int index, JToken item) + { + AddKey(GetKeyForItem(item), item); + base.InsertItem(index, item); + } + + public bool Remove(string key) + { + if (key == null) + throw new ArgumentNullException("key"); + + if (_dictionary != null) + return _dictionary.ContainsKey(key) && Remove(_dictionary[key]); + + return false; + } + + protected override void RemoveItem(int index) + { + string keyForItem = GetKeyForItem(Items[index]); + RemoveKey(keyForItem); + base.RemoveItem(index); + } + + private void RemoveKey(string key) + { + if (_dictionary != null) + _dictionary.Remove(key); + } + + protected override void SetItem(int index, JToken item) + { + string keyForItem = GetKeyForItem(item); + string keyAtIndex = GetKeyForItem(Items[index]); + + if (Comparer.Equals(keyAtIndex, keyForItem)) + { + if (_dictionary != null) + _dictionary[keyForItem] = item; + } + else + { + AddKey(keyForItem, item); + + if (keyAtIndex != null) + RemoveKey(keyAtIndex); + } + base.SetItem(index, item); + } + + public JToken this[string key] + { + get + { + if (key == null) + throw new ArgumentNullException("key"); + + if (_dictionary != null) + return _dictionary[key]; + + throw new KeyNotFoundException(); + } + } + + public bool TryGetValue(string key, out JToken value) + { + if (_dictionary == null) + { + value = null; + return false; + } + + return _dictionary.TryGetValue(key, out value); + } + + public ICollection Keys + { + get + { + EnsureDictionary(); + return _dictionary.Keys; + } + } + + public ICollection Values + { + get + { + EnsureDictionary(); + return _dictionary.Values; + } + } + + public bool Compare(JPropertyKeyedCollection other) + { + if (this == other) + return true; + + // dictionaries in JavaScript aren't ordered + // ignore order when comparing properties + Dictionary d1 = _dictionary; + Dictionary d2 = other._dictionary; + + if (d1 == null && d2 == null) + return true; + + if (d1 == null) + return (d2.Count == 0); + + if (d2 == null) + return (d1.Count == 0); + + if (d1.Count != d2.Count) + return false; + + foreach (KeyValuePair keyAndProperty in d1) + { + JToken secondValue; + if (!d2.TryGetValue(keyAndProperty.Key, out secondValue)) + return false; + + JProperty p1 = (JProperty)keyAndProperty.Value; + JProperty p2 = (JProperty)secondValue; + + if (p1.Value == null) + return (p2.Value == null); + + if (!p1.Value.DeepEquals(p2.Value)) + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JRaw.cs b/Libs/JsonNet/Linq/JRaw.cs new file mode 100644 index 0000000..fec5a57 --- /dev/null +++ b/Libs/JsonNet/Linq/JRaw.cs @@ -0,0 +1,75 @@ +#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.Globalization; +using System.IO; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a raw JSON string. + /// + public class JRaw : JValue + { + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JRaw(JRaw other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The raw json. + public JRaw(object rawJson) + : base(rawJson, JTokenType.Raw) + { + } + + /// + /// Creates an instance of with the content of the reader's current token. + /// + /// The reader. + /// An instance of with the content of the reader's current token. + public static JRaw Create(JsonReader reader) + { + using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(reader); + + return new JRaw(sw.ToString()); + } + } + + internal override JToken CloneToken() + { + return new JRaw(this); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JToken.cs b/Libs/JsonNet/Linq/JToken.cs new file mode 100644 index 0000000..9778949 --- /dev/null +++ b/Libs/JsonNet/Linq/JToken.cs @@ -0,0 +1,2051 @@ +#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 Newtonsoft.Json.Linq.JsonPath; +#if !(NET35 || NET20 || PORTABLE40) +using System.Dynamic; +using System.Linq.Expressions; +#endif +using System.IO; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Diagnostics; +using System.Globalization; +using System.Collections; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents an abstract JSON token. + /// + public abstract class JToken : IJEnumerable, IJsonLineInfo +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + , ICloneable +#endif +#if !(NET35 || NET20 || PORTABLE40) + , IDynamicMetaObjectProvider +#endif + { + private JContainer _parent; + private JToken _previous; + private JToken _next; + private static JTokenEqualityComparer _equalityComparer; + + private int? _lineNumber; + private int? _linePosition; + + private static readonly JTokenType[] BooleanTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean }; + private static readonly JTokenType[] NumberTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean }; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + private static readonly JTokenType[] BigIntegerTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes }; +#endif + private static readonly JTokenType[] StringTypes = new[] { JTokenType.Date, JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Boolean, JTokenType.Bytes, JTokenType.Guid, JTokenType.TimeSpan, JTokenType.Uri }; + private static readonly JTokenType[] GuidTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Guid, JTokenType.Bytes }; + private static readonly JTokenType[] TimeSpanTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.TimeSpan }; + private static readonly JTokenType[] UriTypes = new[] { JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Uri }; + private static readonly JTokenType[] CharTypes = new[] { JTokenType.Integer, JTokenType.Float, JTokenType.String, JTokenType.Comment, JTokenType.Raw }; + private static readonly JTokenType[] DateTimeTypes = new[] { JTokenType.Date, JTokenType.String, JTokenType.Comment, JTokenType.Raw }; + private static readonly JTokenType[] BytesTypes = new[] { JTokenType.Bytes, JTokenType.String, JTokenType.Comment, JTokenType.Raw, JTokenType.Integer }; + + /// + /// Gets a comparer that can compare two tokens for value equality. + /// + /// A that can compare two nodes for value equality. + public static JTokenEqualityComparer EqualityComparer + { + get + { + if (_equalityComparer == null) + _equalityComparer = new JTokenEqualityComparer(); + + return _equalityComparer; + } + } + + /// + /// Gets or sets the parent. + /// + /// The parent. + public JContainer Parent + { + [DebuggerStepThrough] + get { return _parent; } + internal set { _parent = value; } + } + + /// + /// Gets the root of this . + /// + /// The root of this . + public JToken Root + { + get + { + JContainer parent = Parent; + if (parent == null) + return this; + + while (parent.Parent != null) + { + parent = parent.Parent; + } + + return parent; + } + } + + internal abstract JToken CloneToken(); + internal abstract bool DeepEquals(JToken node); + + /// + /// Gets the node type for this . + /// + /// The type. + public abstract JTokenType Type { get; } + + /// + /// Gets a value indicating whether this token has child tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public abstract bool HasValues { get; } + + /// + /// Compares the values of two tokens, including the values of all descendant tokens. + /// + /// The first to compare. + /// The second to compare. + /// true if the tokens are equal; otherwise false. + public static bool DeepEquals(JToken t1, JToken t2) + { + return (t1 == t2 || (t1 != null && t2 != null && t1.DeepEquals(t2))); + } + + /// + /// Gets the next sibling token of this node. + /// + /// The that contains the next sibling token. + public JToken Next + { + get { return _next; } + internal set { _next = value; } + } + + /// + /// Gets the previous sibling token of this node. + /// + /// The that contains the previous sibling token. + public JToken Previous + { + get { return _previous; } + internal set { _previous = value; } + } + + /// + /// Gets the path of the JSON token. + /// + public string Path + { + get + { + if (Parent == null) + return string.Empty; + + IList ancestors = Ancestors().Reverse().ToList(); + ancestors.Add(this); + + IList positions = new List(); + for (int i = 0; i < ancestors.Count; i++) + { + JToken current = ancestors[i]; + JToken next = null; + if (i + 1 < ancestors.Count) + next = ancestors[i + 1]; + else if (ancestors[i].Type == JTokenType.Property) + next = ancestors[i]; + + if (next != null) + { + switch (current.Type) + { + case JTokenType.Property: + JProperty property = (JProperty)current; + positions.Add(new JsonPosition(JsonContainerType.Object) { PropertyName = property.Name }); + break; + case JTokenType.Array: + case JTokenType.Constructor: + int index = ((IList)current).IndexOf(next); + + positions.Add(new JsonPosition(JsonContainerType.Array) { Position = index }); + break; + } + } + } + + return JsonPosition.BuildPath(positions); + } + } + + internal JToken() + { + } + + /// + /// Adds the specified content immediately after this token. + /// + /// A content object that contains simple content or a collection of content objects to be added after this token. + public void AddAfterSelf(object content) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + int index = _parent.IndexOfItem(this); + _parent.AddInternal(index + 1, content, false); + } + + /// + /// Adds the specified content immediately before this token. + /// + /// A content object that contains simple content or a collection of content objects to be added before this token. + public void AddBeforeSelf(object content) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + int index = _parent.IndexOfItem(this); + _parent.AddInternal(index, content, false); + } + + /// + /// Returns a collection of the ancestor tokens of this token. + /// + /// A collection of the ancestor tokens of this token. + public IEnumerable Ancestors() + { + for (JToken parent = Parent; parent != null; parent = parent.Parent) + { + yield return parent; + } + } + + /// + /// Returns a collection of the sibling tokens after this token, in document order. + /// + /// A collection of the sibling tokens after this tokens, in document order. + public IEnumerable AfterSelf() + { + if (Parent == null) + yield break; + + for (JToken o = Next; o != null; o = o.Next) + { + yield return o; + } + } + + /// + /// Returns a collection of the sibling tokens before this token, in document order. + /// + /// A collection of the sibling tokens before this token, in document order. + public IEnumerable BeforeSelf() + { + for (JToken o = Parent.First; o != this; o = o.Next) + { + yield return o; + } + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public virtual JToken this[object key] + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + set { throw new InvalidOperationException("Cannot set child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Gets the with the specified key converted to the specified type. + /// + /// The type to convert the token to. + /// The token key. + /// The converted token value. + public virtual T Value(object key) + { + JToken token = this[key]; + + // null check to fix MonoTouch issue - https://github.com/dolbz/Newtonsoft.Json/commit/a24e3062846b30ee505f3271ac08862bb471b822 + return token == null ? default(T) : Extensions.Convert(token); + } + + /// + /// Get the first child token of this token. + /// + /// A containing the first child token of the . + public virtual JToken First + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Get the last child token of this token. + /// + /// A containing the last child token of the . + public virtual JToken Last + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Returns a collection of the child tokens of this token, in document order. + /// + /// An of containing the child tokens of this , in document order. + public virtual JEnumerable Children() + { + return JEnumerable.Empty; + } + + /// + /// Returns a collection of the child tokens of this token, in document order, filtered by the specified type. + /// + /// The type to filter the child tokens on. + /// A containing the child tokens of this , in document order. + public JEnumerable Children() where T : JToken + { + return new JEnumerable(Children().OfType()); + } + + /// + /// Returns a collection of the child values of this token, in document order. + /// + /// The type to convert the values to. + /// A containing the child values of this , in document order. + public virtual IEnumerable Values() + { + throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); + } + + /// + /// Removes this token from its parent. + /// + public void Remove() + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + _parent.RemoveItem(this); + } + + /// + /// Replaces this token with the specified token. + /// + /// The value. + public void Replace(JToken value) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + _parent.ReplaceItem(this, value); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public abstract void WriteTo(JsonWriter writer, params JsonConverter[] converters); + + /// + /// Returns the indented JSON for this token. + /// + /// + /// The indented JSON for this token. + /// + public override string ToString() + { + return ToString(Formatting.Indented); + } + + /// + /// Returns the JSON for this token using the given formatting and converters. + /// + /// Indicates how the output is formatted. + /// A collection of which will be used when writing the token. + /// The JSON for this token using the given formatting and converters. + public string ToString(Formatting formatting, params JsonConverter[] converters) + { + using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) + { + JsonTextWriter jw = new JsonTextWriter(sw); + jw.Formatting = formatting; + + WriteTo(jw, converters); + + return sw.ToString(); + } + } + + private static JValue EnsureValue(JToken value) + { + if (value == null) + throw new ArgumentNullException("value"); + + if (value is JProperty) + value = ((JProperty)value).Value; + + JValue v = value as JValue; + + return v; + } + + private static string GetType(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + if (token is JProperty) + token = ((JProperty)token).Value; + + return token.Type.ToString(); + } + + private static bool ValidateToken(JToken o, JTokenType[] validTypes, bool nullable) + { + return (Array.IndexOf(validTypes, o.Type) != -1) || (nullable && (o.Type == JTokenType.Null || o.Type == JTokenType.Undefined)); + } + + #region Cast from operators + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator bool(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, BooleanTypes, false)) + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return Convert.ToBoolean((int)(BigInteger)v.Value); +#endif + + return Convert.ToBoolean(v.Value, CultureInfo.InvariantCulture); + } + +#if !NET20 + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTimeOffset(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, DateTimeTypes, false)) + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value is DateTimeOffset) + return (DateTimeOffset)v.Value; + if (v.Value is string) + return DateTimeOffset.Parse((string)v.Value, CultureInfo.InvariantCulture); + return new DateTimeOffset(Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture)); + } +#endif + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator bool?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, BooleanTypes, true)) + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return Convert.ToBoolean((int)(BigInteger)v.Value); +#endif + + return (v.Value != null) ? (bool?)Convert.ToBoolean(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator long(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (long)(BigInteger)v.Value; +#endif + + return Convert.ToInt64(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTime?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, DateTimeTypes, true)) + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !NET20 + if (v.Value is DateTimeOffset) + return ((DateTimeOffset)v.Value).DateTime; +#endif + + return (v.Value != null) ? (DateTime?)Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture) : null; + } + +#if !NET20 + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTimeOffset?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, DateTimeTypes, true)) + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + if (v.Value is DateTimeOffset) + return (DateTimeOffset?)v.Value; + if (v.Value is string) + return DateTimeOffset.Parse((string)v.Value, CultureInfo.InvariantCulture); + return new DateTimeOffset(Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture)); + } +#endif + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator decimal?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (decimal?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (decimal?)Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator double?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (double?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (double?)Convert.ToDouble(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator char?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, CharTypes, true)) + throw new ArgumentException("Can not convert {0} to Char.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (char?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (char?)Convert.ToChar(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator int(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (int)(BigInteger)v.Value; +#endif + + return Convert.ToInt32(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (short)(BigInteger)v.Value; +#endif + + return Convert.ToInt16(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (ushort)(BigInteger)v.Value; +#endif + + return Convert.ToUInt16(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator char(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, CharTypes, false)) + throw new ArgumentException("Can not convert {0} to Char.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (char)(BigInteger)v.Value; +#endif + + return Convert.ToChar(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator byte(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Byte.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (byte)(BigInteger)v.Value; +#endif + + return Convert.ToByte(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator sbyte(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to SByte.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (sbyte)(BigInteger)v.Value; +#endif + + return Convert.ToSByte(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator int?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (int?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (int?)Convert.ToInt32(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (short?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (short?)Convert.ToInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (ushort?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (ushort?)Convert.ToUInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator byte?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Byte.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (byte?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (byte?)Convert.ToByte(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator sbyte?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to SByte.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (sbyte?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (sbyte?)Convert.ToSByte(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTime(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, DateTimeTypes, false)) + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !NET20 + if (v.Value is DateTimeOffset) + return ((DateTimeOffset)v.Value).DateTime; +#endif + + return Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator long?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (long?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (long?)Convert.ToInt64(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator float?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (float?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (float?)Convert.ToSingle(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator decimal(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (decimal)(BigInteger)v.Value; +#endif + + return Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator uint?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (uint?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (uint?)Convert.ToUInt32(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ulong?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, true)) + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (ulong?)(BigInteger)v.Value; +#endif + + return (v.Value != null) ? (ulong?)Convert.ToUInt64(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator double(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (double)(BigInteger)v.Value; +#endif + + return Convert.ToDouble(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator float(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (float)(BigInteger)v.Value; +#endif + + return Convert.ToSingle(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator string(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, StringTypes, true)) + throw new ArgumentException("Can not convert {0} to String.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + if (v.Value is byte[]) + return Convert.ToBase64String((byte[])v.Value); +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return ((BigInteger)v.Value).ToString(CultureInfo.InvariantCulture); +#endif + + return Convert.ToString(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator uint(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (uint)(BigInteger)v.Value; +#endif + + return Convert.ToUInt32(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ulong(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, NumberTypes, false)) + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return (ulong)(BigInteger)v.Value; +#endif + + return Convert.ToUInt64(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to []. + /// + /// The value. + /// The result of the conversion. + public static explicit operator byte[](JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, BytesTypes, false)) + throw new ArgumentException("Can not convert {0} to byte array.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value is string) + return Convert.FromBase64String(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (v.Value is BigInteger) + return ((BigInteger)v.Value).ToByteArray(); +#endif + + if (v.Value is byte[]) + return (byte[])v.Value; + + throw new ArgumentException("Can not convert {0} to byte array.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Guid(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, GuidTypes, false)) + throw new ArgumentException("Can not convert {0} to Guid.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value is byte[]) + return new Guid((byte[])v.Value); + + return (v.Value is Guid) ? (Guid)v.Value : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Guid?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, GuidTypes, true)) + throw new ArgumentException("Can not convert {0} to Guid.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + + if (v.Value is byte[]) + return new Guid((byte[])v.Value); + + return (v.Value is Guid) ? (Guid)v.Value : new Guid(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator TimeSpan(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, TimeSpanTypes, false)) + throw new ArgumentException("Can not convert {0} to TimeSpan.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value is TimeSpan) ? (TimeSpan)v.Value : ConvertUtils.ParseTimeSpan(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator TimeSpan?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, TimeSpanTypes, true)) + throw new ArgumentException("Can not convert {0} to TimeSpan.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + + return (v.Value is TimeSpan) ? (TimeSpan)v.Value : ConvertUtils.ParseTimeSpan(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator Uri(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, UriTypes, true)) + throw new ArgumentException("Can not convert {0} to Uri.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + + return (v.Value is Uri) ? (Uri)v.Value : new Uri(Convert.ToString(v.Value, CultureInfo.InvariantCulture)); + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + private static BigInteger ToBigInteger(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, BigIntegerTypes, false)) + throw new ArgumentException("Can not convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return ConvertUtils.ToBigInteger(v.Value); + } + + private static BigInteger? ToBigIntegerNullable(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateToken(v, BigIntegerTypes, true)) + throw new ArgumentException("Can not convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + if (v.Value == null) + return null; + + return ConvertUtils.ToBigInteger(v.Value); + } +#endif + #endregion + + #region Cast to operators + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(bool value) + { + return new JValue(value); + } + +#if !NET20 + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTimeOffset value) + { + return new JValue(value); + } +#endif + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(byte value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(byte? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(sbyte value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(sbyte? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(bool? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(long value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTime? value) + { + return new JValue(value); + } + +#if !NET20 + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTimeOffset? value) + { + return new JValue(value); + } +#endif + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(decimal? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(double? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ushort value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(int value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(int? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTime value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(long? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(float? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(decimal value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ushort? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(uint? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ulong? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(double value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(float value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(string value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(uint value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ulong value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from [] to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(byte[] value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(Uri value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(TimeSpan value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(TimeSpan? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(Guid value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(Guid? value) + { + return new JValue(value); + } + #endregion + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Children().GetEnumerator(); + } + + internal abstract int GetDeepHashCode(); + + IJEnumerable IJEnumerable.this[object key] + { + get { return this[key]; } + } + + /// + /// Creates an for this token. + /// + /// An that can be used to read this token and its descendants. + public JsonReader CreateReader() + { + return new JTokenReader(this, Path); + } + + internal static JToken FromObjectInternal(object o, JsonSerializer jsonSerializer) + { + ValidationUtils.ArgumentNotNull(o, "o"); + ValidationUtils.ArgumentNotNull(jsonSerializer, "jsonSerializer"); + + JToken token; + using (JTokenWriter jsonWriter = new JTokenWriter()) + { + jsonSerializer.Serialize(jsonWriter, o); + token = jsonWriter.Token; + } + + return token; + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the value of the specified object + public static JToken FromObject(object o) + { + return FromObjectInternal(o, JsonSerializer.CreateDefault()); + } + + /// + /// Creates a from an object using the specified . + /// + /// The object that will be used to create . + /// The that will be used when reading the object. + /// A with the value of the specified object + public static JToken FromObject(object o, JsonSerializer jsonSerializer) + { + return FromObjectInternal(o, jsonSerializer); + } + + /// + /// Creates the specified .NET type from the . + /// + /// The object type that the token will be deserialized to. + /// The new object created from the JSON value. + public T ToObject() + { + return (T)ToObject(typeof(T)); + } + + /// + /// Creates the specified .NET type from the . + /// + /// The object type that the token will be deserialized to. + /// The new object created from the JSON value. + public object ToObject(Type objectType) + { + if (JsonConvert.DefaultSettings == null) + { + bool isEnum; + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(objectType, out isEnum); + + if (isEnum && Type == JTokenType.String) + { + Type enumType = objectType.IsEnum() ? objectType : Nullable.GetUnderlyingType(objectType); + try + { + return Enum.Parse(enumType, (string)this, true); + } + catch (Exception ex) + { + throw new ArgumentException("Could not convert '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, (string)this, enumType.Name), ex); + } + } + + switch (typeCode) + { + case PrimitiveTypeCode.BooleanNullable: + return (bool?)this; + case PrimitiveTypeCode.Boolean: + return (bool)this; + case PrimitiveTypeCode.CharNullable: + return (char?)this; + case PrimitiveTypeCode.Char: + return (char)this; + case PrimitiveTypeCode.SByte: + return (sbyte?)this; + case PrimitiveTypeCode.SByteNullable: + return (sbyte)this; + case PrimitiveTypeCode.ByteNullable: + return (byte?)this; + case PrimitiveTypeCode.Byte: + return (byte)this; + case PrimitiveTypeCode.Int16Nullable: + return (short?)this; + case PrimitiveTypeCode.Int16: + return (short)this; + case PrimitiveTypeCode.UInt16Nullable: + return (ushort?)this; + case PrimitiveTypeCode.UInt16: + return (ushort)this; + case PrimitiveTypeCode.Int32Nullable: + return (int?)this; + case PrimitiveTypeCode.Int32: + return (int)this; + case PrimitiveTypeCode.UInt32Nullable: + return (uint?)this; + case PrimitiveTypeCode.UInt32: + return (uint)this; + case PrimitiveTypeCode.Int64Nullable: + return (long?)this; + case PrimitiveTypeCode.Int64: + return (long)this; + case PrimitiveTypeCode.UInt64Nullable: + return (ulong?)this; + case PrimitiveTypeCode.UInt64: + return (ulong)this; + case PrimitiveTypeCode.SingleNullable: + return (float?)this; + case PrimitiveTypeCode.Single: + return (float)this; + case PrimitiveTypeCode.DoubleNullable: + return (double?)this; + case PrimitiveTypeCode.Double: + return (double)this; + case PrimitiveTypeCode.DecimalNullable: + return (decimal?)this; + case PrimitiveTypeCode.Decimal: + return (decimal)this; + case PrimitiveTypeCode.DateTimeNullable: + return (DateTime?)this; + case PrimitiveTypeCode.DateTime: + return (DateTime)this; +#if !NET20 + case PrimitiveTypeCode.DateTimeOffsetNullable: + return (DateTimeOffset?)this; + case PrimitiveTypeCode.DateTimeOffset: + return (DateTimeOffset)this; +#endif + case PrimitiveTypeCode.String: + return (string)this; + case PrimitiveTypeCode.GuidNullable: + return (Guid?)this; + case PrimitiveTypeCode.Guid: + return (Guid)this; + case PrimitiveTypeCode.Uri: + return (Uri)this; + case PrimitiveTypeCode.TimeSpanNullable: + return (TimeSpan?)this; + case PrimitiveTypeCode.TimeSpan: + return (TimeSpan)this; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + case PrimitiveTypeCode.BigIntegerNullable: + return ToBigIntegerNullable(this); + case PrimitiveTypeCode.BigInteger: + return ToBigInteger(this); +#endif + } + } + + return ToObject(objectType, JsonSerializer.CreateDefault()); + } + + /// + /// Creates the specified .NET type from the using the specified . + /// + /// The object type that the token will be deserialized to. + /// The that will be used when creating the object. + /// The new object created from the JSON value. + public T ToObject(JsonSerializer jsonSerializer) + { + return (T)ToObject(typeof(T), jsonSerializer); + } + + /// + /// Creates the specified .NET type from the using the specified . + /// + /// The object type that the token will be deserialized to. + /// The that will be used when creating the object. + /// The new object created from the JSON value. + public object ToObject(Type objectType, JsonSerializer jsonSerializer) + { + ValidationUtils.ArgumentNotNull(jsonSerializer, "jsonSerializer"); + + using (JTokenReader jsonReader = new JTokenReader(this)) + { + return jsonSerializer.Deserialize(jsonReader, objectType); + } + } + + /// + /// Creates a from a . + /// + /// An positioned at the token to read into this . + /// + /// An that contains the token and its descendant tokens + /// that were read from the reader. The runtime type of the token is determined + /// by the token type of the first token encountered in the reader. + /// + public static JToken ReadFrom(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw JsonReaderException.Create(reader, "Error reading JToken from JsonReader."); + } + + IJsonLineInfo lineInfo = reader as IJsonLineInfo; + + switch (reader.TokenType) + { + case JsonToken.StartObject: + return JObject.Load(reader); + case JsonToken.StartArray: + return JArray.Load(reader); + case JsonToken.StartConstructor: + return JConstructor.Load(reader); + case JsonToken.PropertyName: + return JProperty.Load(reader); + case JsonToken.String: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Date: + case JsonToken.Boolean: + case JsonToken.Bytes: + JValue v = new JValue(reader.Value); + v.SetLineInfo(lineInfo); + return v; + case JsonToken.Comment: + v = JValue.CreateComment(reader.Value.ToString()); + v.SetLineInfo(lineInfo); + return v; + case JsonToken.Null: + v = JValue.CreateNull(); + v.SetLineInfo(lineInfo); + return v; + case JsonToken.Undefined: + v = JValue.CreateUndefined(); + v.SetLineInfo(lineInfo); + return v; + default: + throw JsonReaderException.Create(reader, "Error reading JToken from JsonReader. Unexpected token: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static JToken Parse(string json) + { + using (JsonReader reader = new JsonTextReader(new StringReader(json))) + { + JToken t = Load(reader); + + if (reader.Read() && reader.TokenType != JsonToken.Comment) + throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content."); + + return t; + } + } + + /// + /// Creates a from a . + /// + /// An positioned at the token to read into this . + /// + /// An that contains the token and its descendant tokens + /// that were read from the reader. The runtime type of the token is determined + /// by the token type of the first token encountered in the reader. + /// + public static JToken Load(JsonReader reader) + { + return ReadFrom(reader); + } + + internal void SetLineInfo(IJsonLineInfo lineInfo) + { + if (lineInfo == null || !lineInfo.HasLineInfo()) + return; + + SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); + } + + internal void SetLineInfo(int lineNumber, int linePosition) + { + _lineNumber = lineNumber; + _linePosition = linePosition; + } + + bool IJsonLineInfo.HasLineInfo() + { + return (_lineNumber != null && _linePosition != null); + } + + int IJsonLineInfo.LineNumber + { + get { return _lineNumber ?? 0; } + } + + int IJsonLineInfo.LinePosition + { + get { return _linePosition ?? 0; } + } + + /// + /// Selects a using a JPath expression. Selects the token that matches the object path. + /// + /// + /// A that contains a JPath expression. + /// + /// A , or null. + public JToken SelectToken(string path) + { + return SelectToken(path, false); + } + + /// + /// Selects a using a JPath expression. Selects the token that matches the object path. + /// + /// + /// A that contains a JPath expression. + /// + /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. + /// A . + public JToken SelectToken(string path, bool errorWhenNoMatch) + { + JPath p = new JPath(path); + + JToken token = null; + foreach (JToken t in p.Evaluate(this, errorWhenNoMatch)) + { + if (token != null) + throw new JsonException("Path returned multiple tokens."); + + token = t; + } + + return token; + } + + /// + /// Selects a collection of elements using a JPath expression. + /// + /// + /// A that contains a JPath expression. + /// + /// An that contains the selected elements. + public IEnumerable SelectTokens(string path) + { + return SelectTokens(path, false); + } + + /// + /// Selects a collection of elements using a JPath expression. + /// + /// + /// A that contains a JPath expression. + /// + /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. + /// An that contains the selected elements. + public IEnumerable SelectTokens(string path, bool errorWhenNoMatch) + { + JPath p = new JPath(path); + return p.Evaluate(this, errorWhenNoMatch); + } + +#if !(NET35 || NET20 || PORTABLE40) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected virtual DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new DynamicProxy(), true); + } + + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) + { + return GetMetaObject(parameter); + } +#endif + +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + object ICloneable.Clone() + { + return DeepClone(); + } +#endif + + /// + /// Creates a new instance of the . All child tokens are recursively cloned. + /// + /// A new instance of the . + public JToken DeepClone() + { + return CloneToken(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JTokenEqualityComparer.cs b/Libs/JsonNet/Linq/JTokenEqualityComparer.cs new file mode 100644 index 0000000..8665b7c --- /dev/null +++ b/Libs/JsonNet/Linq/JTokenEqualityComparer.cs @@ -0,0 +1,62 @@ +#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.Collections.Generic; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Compares tokens to determine whether they are equal. + /// + public class JTokenEqualityComparer : IEqualityComparer + { + /// + /// Determines whether the specified objects are equal. + /// + /// The first object of type to compare. + /// The second object of type to compare. + /// + /// true if the specified objects are equal; otherwise, false. + /// + public bool Equals(JToken x, JToken y) + { + return JToken.DeepEquals(x, y); + } + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + /// The type of is a reference type and is null. + public int GetHashCode(JToken obj) + { + if (obj == null) + return 0; + + return obj.GetDeepHashCode(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JTokenReader.cs b/Libs/JsonNet/Linq/JTokenReader.cs new file mode 100644 index 0000000..dbeb094 --- /dev/null +++ b/Libs/JsonNet/Linq/JTokenReader.cs @@ -0,0 +1,350 @@ +#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 Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class JTokenReader : JsonReader, IJsonLineInfo + { + private readonly string _initialPath; + private readonly JToken _root; + private JToken _parent; + internal JToken _current; + + /// + /// Initializes a new instance of the class. + /// + /// The token to read from. + public JTokenReader(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + _root = token; + _current = token; + } + + internal JTokenReader(JToken token, string initialPath) + : this(token) + { + _initialPath = initialPath; + } + + /// + /// Reads the next JSON token from the stream as a []. + /// + /// + /// A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. + /// + public override byte[] ReadAsBytes() + { + return ReadAsBytesInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override decimal? ReadAsDecimal() + { + return ReadAsDecimalInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override int? ReadAsInt32() + { + return ReadAsInt32Internal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override string ReadAsString() + { + return ReadAsStringInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTime? ReadAsDateTime() + { + return ReadAsDateTimeInternal(); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . This method will return null at the end of an array. + public override DateTimeOffset? ReadAsDateTimeOffset() + { + return ReadAsDateTimeOffsetInternal(); + } +#endif + + internal override bool ReadInternal() + { + 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; + } + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + _readType = ReadType.Read; + + return ReadInternal(); + } + + 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() + { + SetToken(JsonToken.None); + return false; + } + + private JsonToken? GetEndToken(JContainer c) + { + switch (c.Type) + { + case JTokenType.Object: + return JsonToken.EndObject; + case JTokenType.Array: + return JsonToken.EndArray; + case JTokenType.Constructor: + return JsonToken.EndConstructor; + case JTokenType.Property: + return null; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("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 JTokenType.Object: + SetToken(JsonToken.StartObject); + break; + case JTokenType.Array: + SetToken(JsonToken.StartArray); + break; + case JTokenType.Constructor: + SetToken(JsonToken.StartConstructor, ((JConstructor)token).Name); + break; + case JTokenType.Property: + SetToken(JsonToken.PropertyName, ((JProperty)token).Name); + break; + case JTokenType.Comment: + SetToken(JsonToken.Comment, ((JValue)token).Value); + break; + case JTokenType.Integer: + SetToken(JsonToken.Integer, ((JValue)token).Value); + break; + case JTokenType.Float: + SetToken(JsonToken.Float, ((JValue)token).Value); + break; + case JTokenType.String: + SetToken(JsonToken.String, ((JValue)token).Value); + break; + case JTokenType.Boolean: + SetToken(JsonToken.Boolean, ((JValue)token).Value); + break; + case JTokenType.Null: + SetToken(JsonToken.Null, ((JValue)token).Value); + break; + case JTokenType.Undefined: + SetToken(JsonToken.Undefined, ((JValue)token).Value); + break; + case JTokenType.Date: + SetToken(JsonToken.Date, ((JValue)token).Value); + break; + case JTokenType.Raw: + SetToken(JsonToken.Raw, ((JValue)token).Value); + break; + case JTokenType.Bytes: + SetToken(JsonToken.Bytes, ((JValue)token).Value); + break; + case JTokenType.Guid: + SetToken(JsonToken.String, SafeToString(((JValue)token).Value)); + break; + case JTokenType.Uri: + SetToken(JsonToken.String, SafeToString(((JValue)token).Value)); + break; + case JTokenType.TimeSpan: + SetToken(JsonToken.String, SafeToString(((JValue)token).Value)); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", token.Type, "Unexpected JTokenType."); + } + } + + private string SafeToString(object value) + { + return (value != null) ? value.ToString() : null; + } + + bool IJsonLineInfo.HasLineInfo() + { + if (CurrentState == State.Start) + return false; + + IJsonLineInfo info = _current; + return (info != null && info.HasLineInfo()); + } + + int IJsonLineInfo.LineNumber + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = _current; + if (info != null) + return info.LineNumber; + + return 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = _current; + if (info != null) + return info.LinePosition; + + return 0; + } + } + + /// + /// Gets the path of the current JSON token. + /// + public override string Path + { + get + { + string path = base.Path; + + if (!string.IsNullOrEmpty(_initialPath)) + { + if (string.IsNullOrEmpty(path)) + return _initialPath; + + if (_initialPath.EndsWith(']') + || path.StartsWith('[')) + path = _initialPath + path; + else + path = _initialPath + "." + path; + } + + return path; + } + } + } +} diff --git a/Libs/JsonNet/Linq/JTokenType.cs b/Libs/JsonNet/Linq/JTokenType.cs new file mode 100644 index 0000000..00e04a4 --- /dev/null +++ b/Libs/JsonNet/Linq/JTokenType.cs @@ -0,0 +1,123 @@ +#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 + +namespace Newtonsoft.Json.Linq +{ + /// + /// Specifies the type of token. + /// + public enum JTokenType + { + /// + /// No token type has been set. + /// + None, + + /// + /// A JSON object. + /// + Object, + + /// + /// A JSON array. + /// + Array, + + /// + /// A JSON constructor. + /// + Constructor, + + /// + /// A JSON object property. + /// + Property, + + /// + /// A comment. + /// + Comment, + + /// + /// An integer value. + /// + Integer, + + /// + /// A float value. + /// + Float, + + /// + /// A string value. + /// + String, + + /// + /// A boolean value. + /// + Boolean, + + /// + /// A null value. + /// + Null, + + /// + /// An undefined value. + /// + Undefined, + + /// + /// A date value. + /// + Date, + + /// + /// A raw JSON value. + /// + Raw, + + /// + /// A collection of bytes value. + /// + Bytes, + + /// + /// A Guid value. + /// + Guid, + + /// + /// A Uri value. + /// + Uri, + + /// + /// A TimeSpan value. + /// + TimeSpan + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JTokenWriter.cs b/Libs/JsonNet/Linq/JTokenWriter.cs new file mode 100644 index 0000000..4bce447 --- /dev/null +++ b/Libs/JsonNet/Linq/JTokenWriter.cs @@ -0,0 +1,458 @@ +#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.Globalization; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public class JTokenWriter : JsonWriter + { + private JContainer _token; + private JContainer _parent; + // used when writer is writing single value and the value has no containing parent + private JValue _value; + + /// + /// Gets the token being writen. + /// + /// The token being writen. + public JToken Token + { + get + { + if (_token != null) + return _token; + + return _value; + } + } + + /// + /// Initializes a new instance of the class writing to the given . + /// + /// The container being written to. + public JTokenWriter(JContainer container) + { + ValidationUtils.ArgumentNotNull(container, "container"); + + _token = container; + _parent = container; + } + + /// + /// Initializes a new instance of the class. + /// + public JTokenWriter() + { + } + + /// + /// 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.AddAndSkipParentCheck(container); + + _parent = container; + } + + private void RemoveParent() + { + _parent = _parent.Parent; + + if (_parent != null && _parent.Type == JTokenType.Property) + _parent = _parent.Parent; + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + AddParent(new JArray()); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + base.WriteStartConstructor(name); + + AddParent(new JConstructor(name)); + } + + /// + /// Writes the end. + /// + /// The token. + protected override void WriteEnd(JsonToken token) + { + RemoveParent(); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + AddParent(new JProperty(name)); + + // don't set state until after in case of an error such as duplicate property names + // incorrect state will cause issues if writer is disposed when closing open properties + base.WritePropertyName(name); + } + + private void AddValue(object value, JsonToken token) + { + AddValue(new JValue(value), token); + } + + internal void AddValue(JValue value, JsonToken token) + { + if (_parent != null) + { + _parent.Add(value); + + if (_parent.Type == JTokenType.Property) + _parent = _parent.Parent; + } + else + { + _value = value ?? JValue.CreateNull(); + } + } + + #region WriteValue methods + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public override void WriteValue(object value) + { +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + InternalWriteValue(JsonToken.Integer); + AddValue(value, JsonToken.Integer); + } + else +#endif + { + base.WriteValue(value); + } + } + + /// + /// 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 JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + base.WriteRaw(json); + AddValue(new JRaw(json), JsonToken.Raw); + } + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + base.WriteComment(text); + AddValue(JValue.CreateComment(text), JsonToken.Comment); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + AddValue(value, 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. + [CLSCompliant(false)] + 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. + [CLSCompliant(false)] + 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. + [CLSCompliant(false)] + 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); + string s = null; +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + s = value.ToString(CultureInfo.InvariantCulture); +#else + s = value.ToString(); +#endif + AddValue(s, 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. + [CLSCompliant(false)] + 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); + value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling); + AddValue(value, JsonToken.Date); + } + +#if !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Date); + } +#endif + + /// + /// Writes a [] value. + /// + /// The [] value to write. + public override void WriteValue(byte[] value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Bytes); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(TimeSpan value) + { + base.WriteValue(value); + AddValue(value, JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Guid value) + { + base.WriteValue(value); + AddValue(value, JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(Uri value) + { + base.WriteValue(value); + AddValue(value, JsonToken.String); + } + #endregion + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JValue.cs b/Libs/JsonNet/Linq/JValue.cs new file mode 100644 index 0000000..ad24700 --- /dev/null +++ b/Libs/JsonNet/Linq/JValue.cs @@ -0,0 +1,1056 @@ +#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 Newtonsoft.Json.Utilities; +using System.Globalization; +#if !(NET35 || NET20 || PORTABLE40) +using System.Dynamic; +using System.Linq.Expressions; +#endif +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; + +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a value in JSON (string, integer, date, etc). + /// + public class JValue : JToken, IEquatable, IFormattable, IComparable, IComparable +#if !(NETFX_CORE || PORTABLE) + , IConvertible +#endif + { + private JTokenType _valueType; + private object _value; + + internal JValue(object value, JTokenType type) + { + _value = value; + _valueType = type; + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JValue(JValue other) + : this(other.Value, other.Type) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(long value) + : this(value, JTokenType.Integer) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(decimal value) + : this(value, JTokenType.Float) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(char value) + : this(value, JTokenType.String) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + [CLSCompliant(false)] + public JValue(ulong value) + : this(value, JTokenType.Integer) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(double value) + : this(value, JTokenType.Float) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(float value) + : this(value, JTokenType.Float) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(DateTime value) + : this(value, JTokenType.Date) + { + } + +#if !NET20 + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(DateTimeOffset value) + : this(value, JTokenType.Date) + { + } +#endif + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(bool value) + : this(value, JTokenType.Boolean) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(string value) + : this(value, JTokenType.String) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(Guid value) + : this(value, JTokenType.Guid) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(Uri value) + : this(value, (value != null) ? JTokenType.Uri : JTokenType.Null) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(TimeSpan value) + : this(value, JTokenType.TimeSpan) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(object value) + : this(value, GetValueType(null, value)) + { + } + + internal override bool DeepEquals(JToken node) + { + JValue other = node as JValue; + if (other == null) + return false; + if (other == this) + return true; + + return ValuesEquals(this, other); + } + + /// + /// Gets a value indicating whether this token has child tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public override bool HasValues + { + get { return false; } + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + private static int CompareBigInteger(BigInteger i1, object i2) + { + int result = i1.CompareTo(ConvertUtils.ToBigInteger(i2)); + + if (result != 0) + return result; + + // converting a fractional number to a BigInteger will lose the fraction + // check for fraction if result is two numbers are equal + if (i2 is decimal) + { + decimal d = (decimal)i2; + return (0m).CompareTo(Math.Abs(d - Math.Truncate(d))); + } + else if (i2 is double || i2 is float) + { + double d = Convert.ToDouble(i2, CultureInfo.InvariantCulture); + return (0d).CompareTo(Math.Abs(d - Math.Truncate(d))); + } + + return result; + } +#endif + + internal static int Compare(JTokenType valueType, object objA, object objB) + { + if (objA == null && objB == null) + return 0; + if (objA != null && objB == null) + return 1; + if (objA == null && objB != null) + return -1; + + switch (valueType) + { + case JTokenType.Integer: +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (objA is BigInteger) + return CompareBigInteger((BigInteger)objA, objB); + if (objB is BigInteger) + return -CompareBigInteger((BigInteger)objB, objA); +#endif + if (objA is ulong || objB is ulong || objA is decimal || objB is decimal) + return Convert.ToDecimal(objA, CultureInfo.InvariantCulture).CompareTo(Convert.ToDecimal(objB, CultureInfo.InvariantCulture)); + else if (objA is float || objB is float || objA is double || objB is double) + return CompareFloat(objA, objB); + else + return Convert.ToInt64(objA, CultureInfo.InvariantCulture).CompareTo(Convert.ToInt64(objB, CultureInfo.InvariantCulture)); + case JTokenType.Float: +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (objA is BigInteger) + return CompareBigInteger((BigInteger)objA, objB); + if (objB is BigInteger) + return -CompareBigInteger((BigInteger)objB, objA); +#endif + return CompareFloat(objA, objB); + case JTokenType.Comment: + case JTokenType.String: + case JTokenType.Raw: + string s1 = Convert.ToString(objA, CultureInfo.InvariantCulture); + string s2 = Convert.ToString(objB, CultureInfo.InvariantCulture); + + return string.CompareOrdinal(s1, s2); + case JTokenType.Boolean: + bool b1 = Convert.ToBoolean(objA, CultureInfo.InvariantCulture); + bool b2 = Convert.ToBoolean(objB, CultureInfo.InvariantCulture); + + return b1.CompareTo(b2); + case JTokenType.Date: +#if !NET20 + if (objA is DateTime) + { +#endif + DateTime date1 = (DateTime)objA; + DateTime date2; + +#if !NET20 + if (objB is DateTimeOffset) + date2 = ((DateTimeOffset)objB).DateTime; + else +#endif + date2 = Convert.ToDateTime(objB, CultureInfo.InvariantCulture); + + return date1.CompareTo(date2); +#if !NET20 + } + else + { + DateTimeOffset date1 = (DateTimeOffset)objA; + DateTimeOffset date2; + + if (objB is DateTimeOffset) + date2 = (DateTimeOffset)objB; + else + date2 = new DateTimeOffset(Convert.ToDateTime(objB, CultureInfo.InvariantCulture)); + + return date1.CompareTo(date2); + } +#endif + case JTokenType.Bytes: + if (!(objB is byte[])) + throw new ArgumentException("Object must be of type byte[]."); + + byte[] bytes1 = objA as byte[]; + byte[] bytes2 = objB as byte[]; + if (bytes1 == null) + return -1; + if (bytes2 == null) + return 1; + + return MiscellaneousUtils.ByteArrayCompare(bytes1, bytes2); + case JTokenType.Guid: + if (!(objB is Guid)) + throw new ArgumentException("Object must be of type Guid."); + + Guid guid1 = (Guid)objA; + Guid guid2 = (Guid)objB; + + return guid1.CompareTo(guid2); + case JTokenType.Uri: + if (!(objB is Uri)) + throw new ArgumentException("Object must be of type Uri."); + + Uri uri1 = (Uri)objA; + Uri uri2 = (Uri)objB; + + return Comparer.Default.Compare(uri1.ToString(), uri2.ToString()); + case JTokenType.TimeSpan: + if (!(objB is TimeSpan)) + throw new ArgumentException("Object must be of type TimeSpan."); + + TimeSpan ts1 = (TimeSpan)objA; + TimeSpan ts2 = (TimeSpan)objB; + + return ts1.CompareTo(ts2); + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("valueType", valueType, "Unexpected value type: {0}".FormatWith(CultureInfo.InvariantCulture, valueType)); + } + } + + private static int CompareFloat(object objA, object objB) + { + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + + // take into account possible floating point errors + if (MathUtils.ApproxEquals(d1, d2)) + return 0; + + return d1.CompareTo(d2); + } + +#if !(NET35 || NET20 || PORTABLE40) + private static bool Operation(ExpressionType operation, object objA, object objB, out object result) + { + if (objA is string || objB is string) + { + if (operation == ExpressionType.Add || operation == ExpressionType.AddAssign) + { + result = ((objA != null) ? objA.ToString() : null) + ((objB != null) ? objB.ToString() : null); + return true; + } + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (objA is BigInteger || objB is BigInteger) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + // not that this will lose the fraction + // BigInteger doesn't have operators with non-integer types + BigInteger i1 = ConvertUtils.ToBigInteger(objA); + BigInteger i2 = ConvertUtils.ToBigInteger(objB); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = i1 + i2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = i1 - i2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = i1 * i2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = i1 / i2; + return true; + } + } + else +#endif + if (objA is ulong || objB is ulong || objA is decimal || objB is decimal) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + decimal d1 = Convert.ToDecimal(objA, CultureInfo.InvariantCulture); + decimal d2 = Convert.ToDecimal(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is float || objB is float || objA is double || objB is double) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is int || objA is uint || objA is long || objA is short || objA is ushort || objA is sbyte || objA is byte || + objB is int || objB is uint || objB is long || objB is short || objB is ushort || objB is sbyte || objB is byte) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + long l1 = Convert.ToInt64(objA, CultureInfo.InvariantCulture); + long l2 = Convert.ToInt64(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = l1 + l2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = l1 - l2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = l1 * l2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = l1 / l2; + return true; + } + } + + result = null; + return false; + } +#endif + + internal override JToken CloneToken() + { + return new JValue(this); + } + + /// + /// Creates a comment with the given value. + /// + /// The value. + /// A comment with the given value. + public static JValue CreateComment(string value) + { + return new JValue(value, JTokenType.Comment); + } + + /// + /// Creates a string with the given value. + /// + /// The value. + /// A string with the given value. + public static JValue CreateString(string value) + { + return new JValue(value, JTokenType.String); + } + + /// + /// Creates a null value. + /// + /// A null value. + public static JValue CreateNull() + { + return new JValue(null, JTokenType.Null); + } + + /// + /// Creates a null value. + /// + /// A null value. + public static JValue CreateUndefined() + { + return new JValue(null, JTokenType.Undefined); + } + + private static JTokenType GetValueType(JTokenType? current, object value) + { + if (value == null) + return JTokenType.Null; +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + else if (value == DBNull.Value) + return JTokenType.Null; +#endif + else if (value is string) + return GetStringValueType(current); + 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 JTokenType.Integer; + else if (value is Enum) + return JTokenType.Integer; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + else if (value is BigInteger) + return JTokenType.Integer; +#endif + else if (value is double || value is float || value is decimal) + return JTokenType.Float; + else if (value is DateTime) + return JTokenType.Date; +#if !NET20 + else if (value is DateTimeOffset) + return JTokenType.Date; +#endif + else if (value is byte[]) + return JTokenType.Bytes; + else if (value is bool) + return JTokenType.Boolean; + else if (value is Guid) + return JTokenType.Guid; + else if (value is Uri) + return JTokenType.Uri; + else if (value is TimeSpan) + return JTokenType.TimeSpan; + + throw new ArgumentException("Could not determine JSON object type for type {0}.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + private static JTokenType GetStringValueType(JTokenType? current) + { + if (current == null) + return JTokenType.String; + + switch (current.Value) + { + case JTokenType.Comment: + case JTokenType.String: + case JTokenType.Raw: + return current.Value; + default: + return JTokenType.String; + } + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return _valueType; } + } + + /// + /// Gets or sets the underlying token value. + /// + /// The underlying token value. + public object Value + { + get { return _value; } + set + { + Type currentType = (_value != null) ? _value.GetType() : null; + Type newType = (value != null) ? value.GetType() : null; + + if (currentType != newType) + _valueType = GetValueType(_valueType, value); + + _value = value; + } + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + if (converters != null && converters.Length > 0 && _value != null) + { + JsonConverter matchingConverter = JsonSerializer.GetMatchingConverter(converters, _value.GetType()); + if (matchingConverter != null && matchingConverter.CanWrite) + { + matchingConverter.WriteJson(writer, _value, JsonSerializer.CreateDefault()); + return; + } + } + + switch (_valueType) + { + case JTokenType.Comment: + writer.WriteComment((_value != null) ? _value.ToString() : null); + return; + case JTokenType.Raw: + writer.WriteRawValue((_value != null) ? _value.ToString() : null); + return; + case JTokenType.Null: + writer.WriteNull(); + return; + case JTokenType.Undefined: + writer.WriteUndefined(); + return; + case JTokenType.Integer: +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (_value is BigInteger) + writer.WriteValue((BigInteger)_value); + else +#endif + writer.WriteValue(Convert.ToInt64(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.Float: + if (_value is decimal) + writer.WriteValue((decimal)_value); + else if (_value is double) + writer.WriteValue((double)_value); + else if (_value is float) + writer.WriteValue((float)_value); + else + writer.WriteValue(Convert.ToDouble(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.String: + writer.WriteValue((_value != null) ? _value.ToString() : null); + return; + case JTokenType.Boolean: + writer.WriteValue(Convert.ToBoolean(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.Date: +#if !NET20 + if (_value is DateTimeOffset) + writer.WriteValue((DateTimeOffset)_value); + else +#endif + writer.WriteValue(Convert.ToDateTime(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.Bytes: + writer.WriteValue((byte[])_value); + return; + case JTokenType.Guid: + case JTokenType.Uri: + case JTokenType.TimeSpan: + writer.WriteValue((_value != null) ? _value.ToString() : null); + return; + } + + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", _valueType, "Unexpected token type."); + } + + internal override int GetDeepHashCode() + { + int valueHashCode = (_value != null) ? _value.GetHashCode() : 0; + + // GetHashCode on an enum boxes so cast to int + return ((int)_valueType).GetHashCode() ^ valueHashCode; + } + + private static bool ValuesEquals(JValue v1, JValue v2) + { + return (v1 == v2 || (v1._valueType == v2._valueType && Compare(v1._valueType, v1._value, v2._value) == 0)); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + /// An object to compare with this object. + public bool Equals(JValue other) + { + if (other == null) + return false; + + return ValuesEquals(this, other); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (obj == null) + return false; + + JValue otherValue = obj as JValue; + if (otherValue != null) + return Equals(otherValue); + + return base.Equals(obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + if (_value == null) + return 0; + + return _value.GetHashCode(); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + if (_value == null) + return string.Empty; + + return _value.ToString(); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return ToString(null, formatProvider); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (_value == null) + return string.Empty; + + IFormattable formattable = _value as IFormattable; + if (formattable != null) + return formattable.ToString(format, formatProvider); + else + return _value.ToString(); + } + +#if !(NET35 || NET20 || PORTABLE40) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new JValueDynamicProxy(), true); + } + + private class JValueDynamicProxy : DynamicProxy + { + public override bool TryConvert(JValue instance, ConvertBinder binder, out object result) + { + if (binder.Type == typeof(JValue)) + { + result = instance; + return true; + } + + object value = instance.Value; + + if (value == null) + { + result = null; + return ReflectionUtils.IsNullable(binder.Type); + } + + result = ConvertUtils.Convert(value, CultureInfo.InvariantCulture, binder.Type); + return true; + } + + public override bool TryBinaryOperation(JValue instance, BinaryOperationBinder binder, object arg, out object result) + { + object compareValue = (arg is JValue) ? ((JValue)arg).Value : arg; + + switch (binder.Operation) + { + case ExpressionType.Equal: + result = (Compare(instance.Type, instance.Value, compareValue) == 0); + return true; + case ExpressionType.NotEqual: + result = (Compare(instance.Type, instance.Value, compareValue) != 0); + return true; + case ExpressionType.GreaterThan: + result = (Compare(instance.Type, instance.Value, compareValue) > 0); + return true; + case ExpressionType.GreaterThanOrEqual: + result = (Compare(instance.Type, instance.Value, compareValue) >= 0); + return true; + case ExpressionType.LessThan: + result = (Compare(instance.Type, instance.Value, compareValue) < 0); + return true; + case ExpressionType.LessThanOrEqual: + result = (Compare(instance.Type, instance.Value, compareValue) <= 0); + return true; + case ExpressionType.Add: + case ExpressionType.AddAssign: + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + if (Operation(binder.Operation, instance.Value, compareValue, out result)) + { + result = new JValue(result); + return true; + } + break; + } + + result = null; + return false; + } + } +#endif + + int IComparable.CompareTo(object obj) + { + if (obj == null) + return 1; + + object otherValue = (obj is JValue) ? ((JValue)obj).Value : obj; + + return Compare(_valueType, _value, otherValue); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// + /// An object to compare with this instance. + /// + /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: + /// Value + /// Meaning + /// Less than zero + /// This instance is less than . + /// Zero + /// This instance is equal to . + /// Greater than zero + /// This instance is greater than . + /// + /// + /// is not the same type as this instance. + /// + public int CompareTo(JValue obj) + { + if (obj == null) + return 1; + + return Compare(_valueType, _value, obj._value); + } + +#if !(NETFX_CORE || PORTABLE) + TypeCode IConvertible.GetTypeCode() + { + if (_value == null) + return TypeCode.Empty; + +#if !NET20 + if (_value is DateTimeOffset) + return TypeCode.DateTime; +#endif +#if !(NET20 || NET35 || PORTABLE40) + if (_value is BigInteger) + return TypeCode.Object; +#endif + + return System.Type.GetTypeCode(_value.GetType()); + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return (bool)this; + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return (char)this; + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return (sbyte)this; + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return (byte)this; + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return (short)this; + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return (ushort)this; + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return (int)this; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return (uint)this; + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return (long)this; + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return (ulong)this; + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return (float)this; + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return (double)this; + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return (decimal)this; + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + return (DateTime)this; + } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + return ToObject(conversionType); + } +#endif + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonMergeSettings.cs b/Libs/JsonNet/Linq/JsonMergeSettings.cs new file mode 100644 index 0000000..3f33c6a --- /dev/null +++ b/Libs/JsonNet/Linq/JsonMergeSettings.cs @@ -0,0 +1,28 @@ +using System; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Specifies the settings used when merging JSON. + /// + public class JsonMergeSettings + { + private MergeArrayHandling _mergeArrayHandling; + + /// + /// Gets or sets the method used when merging JSON arrays. + /// + /// The method used when merging JSON arrays. + public MergeArrayHandling MergeArrayHandling + { + get { return _mergeArrayHandling; } + set + { + if (value < MergeArrayHandling.Concat || value > MergeArrayHandling.Merge) + throw new ArgumentOutOfRangeException("value"); + + _mergeArrayHandling = value; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/ArrayIndexFilter.cs b/Libs/JsonNet/Linq/JsonPath/ArrayIndexFilter.cs new file mode 100644 index 0000000..6206edc --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/ArrayIndexFilter.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class ArrayIndexFilter : PathFilter + { + public int? Index { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken t in current) + { + if (Index != null) + { + JToken v = GetTokenIndex(t, errorWhenNoMatch, Index.Value); + + if (v != null) + yield return v; + } + else + { + if (t is JArray || t is JConstructor) + { + foreach (JToken v in t) + { + yield return v; + } + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Index * not valid on {0}.".FormatWith(CultureInfo.InvariantCulture, t.GetType().Name)); + } + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/ArrayMultipleIndexFilter.cs b/Libs/JsonNet/Linq/JsonPath/ArrayMultipleIndexFilter.cs new file mode 100644 index 0000000..fe01f14 --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/ArrayMultipleIndexFilter.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class ArrayMultipleIndexFilter : PathFilter + { + public List Indexes { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken t in current) + { + foreach (int i in Indexes) + { + JToken v = GetTokenIndex(t, errorWhenNoMatch, i); + + if (v != null) + yield return v; + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/ArraySliceFilter.cs b/Libs/JsonNet/Linq/JsonPath/ArraySliceFilter.cs new file mode 100644 index 0000000..ee229de --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/ArraySliceFilter.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class ArraySliceFilter : PathFilter + { + public int? Start { get; set; } + public int? End { get; set; } + public int? Step { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + if (Step == 0) + throw new JsonException("Step cannot be zero."); + + foreach (JToken t in current) + { + JArray a = t as JArray; + if (a != null) + { + // set defaults for null arguments + int stepCount = Step ?? 1; + int startIndex = Start ?? ((stepCount > 0) ? 0 : a.Count - 1); + int stopIndex = End ?? ((stepCount > 0) ? a.Count : -1); + + // start from the end of the list if start is negitive + if (Start < 0) startIndex = a.Count + startIndex; + + // end from the start of the list if stop is negitive + if (End < 0) stopIndex = a.Count + stopIndex; + + // ensure indexes keep within collection bounds + startIndex = Math.Max(startIndex, (stepCount > 0) ? 0 : int.MinValue); + startIndex = Math.Min(startIndex, (stepCount > 0) ? a.Count : a.Count - 1); + stopIndex = Math.Max(stopIndex, -1); + stopIndex = Math.Min(stopIndex, a.Count); + + bool positiveStep = (stepCount > 0); + + if (IsValid(startIndex, stopIndex, positiveStep)) + { + for (int i = startIndex; IsValid(i, stopIndex, positiveStep); i += stepCount) + { + yield return a[i]; + } + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Array slice of {0} to {1} returned no results.".FormatWith(CultureInfo.InvariantCulture, + Start != null ? Start.Value.ToString(CultureInfo.InvariantCulture) : "*", + End != null ? End.Value.ToString(CultureInfo.InvariantCulture) : "*")); + } + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Array slice is not valid on {0}.".FormatWith(CultureInfo.InvariantCulture, t.GetType().Name)); + } + } + } + + private bool IsValid(int index, int stopIndex, bool positiveStep) + { + if (positiveStep) + return (index < stopIndex); + + return (index > stopIndex); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/FieldFilter.cs b/Libs/JsonNet/Linq/JsonPath/FieldFilter.cs new file mode 100644 index 0000000..d3fe1f0 --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/FieldFilter.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class FieldFilter : PathFilter + { + public string Name { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken t in current) + { + JObject o = t as JObject; + if (o != null) + { + if (Name != null) + { + JToken v = o[Name]; + + if (v != null) + yield return v; + else if (errorWhenNoMatch) + throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, Name)); + } + else + { + foreach (KeyValuePair p in o) + { + yield return p.Value; + } + } + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, Name ?? "*", t.GetType().Name)); + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/FieldMultipleFilter.cs b/Libs/JsonNet/Linq/JsonPath/FieldMultipleFilter.cs new file mode 100644 index 0000000..8349f4a --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/FieldMultipleFilter.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class FieldMultipleFilter : PathFilter + { + public List Names { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken t in current) + { + JObject o = t as JObject; + if (o != null) + { + foreach (string name in Names) + { + JToken v = o[name]; + + if (v != null) + yield return v; + + if (errorWhenNoMatch) + throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, name)); + } + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Properties {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", Names.Select(n => "'" + n + "'").ToArray()), t.GetType().Name)); + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/JPath.cs b/Libs/JsonNet/Linq/JsonPath/JPath.cs new file mode 100644 index 0000000..0941f3e --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/JPath.cs @@ -0,0 +1,674 @@ +#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.Globalization; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class JPath + { + private readonly string _expression; + public List Filters { get; private set; } + + private int _currentIndex; + + public JPath(string expression) + { + ValidationUtils.ArgumentNotNull(expression, "expression"); + _expression = expression; + Filters = new List(); + + ParseMain(); + } + + private void ParseMain() + { + int currentPartStartIndex = _currentIndex; + + EatWhitespace(); + + if (_expression.Length == _currentIndex) + return; + + if (_expression[_currentIndex] == '$') + { + if (_expression.Length == 1) + return; + + // only increment position for "$." or "$[" + // otherwise assume property that starts with $ + char c = _expression[_currentIndex + 1]; + if (c == '.' || c == '[') + { + _currentIndex++; + currentPartStartIndex = _currentIndex; + } + } + + if (!ParsePath(Filters, currentPartStartIndex, false)) + { + int lastCharacterIndex = _currentIndex; + + EatWhitespace(); + + if (_currentIndex < _expression.Length) + throw new JsonException("Unexpected character while parsing path: " + _expression[lastCharacterIndex]); + } + } + + private bool ParsePath(List filters, int currentPartStartIndex, bool query) + { + bool scan = false; + bool followingIndexer = false; + bool followingDot = false; + + bool ended = false; + while (_currentIndex < _expression.Length && !ended) + { + char currentChar = _expression[_currentIndex]; + + switch (currentChar) + { + case '[': + case '(': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member }; + filters.Add(filter); + scan = false; + } + + filters.Add(ParseIndexer(currentChar)); + _currentIndex++; + currentPartStartIndex = _currentIndex; + followingIndexer = true; + followingDot = false; + break; + case ']': + case ')': + ended = true; + break; + case ' ': + //EatWhitespace(); + if (_currentIndex < _expression.Length) + ended = true; + break; + case '.': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + if (member == "*") + member = null; + PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member }; + filters.Add(filter); + scan = false; + } + if (_currentIndex + 1 < _expression.Length && _expression[_currentIndex + 1] == '.') + { + scan = true; + _currentIndex++; + } + _currentIndex++; + currentPartStartIndex = _currentIndex; + followingIndexer = false; + followingDot = true; + break; + default: + if (query && (currentChar == '=' || currentChar == '<' || currentChar == '!' || currentChar == '>' || currentChar == '|' || currentChar == '&')) + { + ended = true; + } + else + { + if (followingIndexer) + throw new JsonException("Unexpected character following indexer: " + currentChar); + + _currentIndex++; + } + break; + } + } + + bool atPathEnd = (_currentIndex == _expression.Length); + + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex).TrimEnd(); + if (member == "*") + member = null; + PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member }; + filters.Add(filter); + } + else + { + // no field name following dot in path and at end of base path/query + if (followingDot && (atPathEnd || query)) + throw new JsonException("Unexpected end while parsing path."); + } + + return atPathEnd; + } + + private PathFilter ParseIndexer(char indexerOpenChar) + { + _currentIndex++; + + char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')'; + + EnsureLength("Path ended with open indexer."); + + EatWhitespace(); + + if (_expression[_currentIndex] == '\'') + { + return ParseQuotedField(indexerCloseChar); + } + else if (_expression[_currentIndex] == '?') + { + return ParseQuery(indexerCloseChar); + } + else + { + return ParseArrayIndexer(indexerCloseChar); + } + } + + private PathFilter ParseArrayIndexer(char indexerCloseChar) + { + int start = _currentIndex; + int? end = null; + List indexes = null; + int colonCount = 0; + int? startIndex = null; + int? endIndex = null; + int? step = null; + + while (_currentIndex < _expression.Length) + { + char currentCharacter = _expression[_currentIndex]; + + if (currentCharacter == ' ') + { + end = _currentIndex; + EatWhitespace(); + continue; + } + + if (currentCharacter == indexerCloseChar) + { + int length = (end ?? _currentIndex) - start; + + if (indexes != null) + { + if (length == 0) + throw new JsonException("Array index expected."); + + string indexer = _expression.Substring(start, length); + int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture); + + indexes.Add(index); + return new ArrayMultipleIndexFilter { Indexes = indexes }; + } + else if (colonCount > 0) + { + if (length > 0) + { + string indexer = _expression.Substring(start, length); + int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture); + + if (colonCount == 1) + endIndex = index; + else + step = index; + } + + return new ArraySliceFilter { Start = startIndex, End = endIndex, Step = step }; + } + else + { + if (length == 0) + throw new JsonException("Array index expected."); + + string indexer = _expression.Substring(start, length); + int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture); + + return new ArrayIndexFilter { Index = index }; + } + } else if (currentCharacter == ',') + { + int length = (end ?? _currentIndex) - start; + + if (length == 0) + throw new JsonException("Array index expected."); + + if (indexes == null) + indexes = new List(); + + string indexer = _expression.Substring(start, length); + indexes.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture)); + + _currentIndex++; + + EatWhitespace(); + + start = _currentIndex; + end = null; + } + else if (currentCharacter == '*') + { + _currentIndex++; + EnsureLength("Path ended with open indexer."); + EatWhitespace(); + + if (_expression[_currentIndex] != indexerCloseChar) + throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter); + + return new ArrayIndexFilter(); + } + else if (currentCharacter == ':') + { + int length = (end ?? _currentIndex) - start; + + if (length > 0) + { + string indexer = _expression.Substring(start, length); + int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture); + + if (colonCount == 0) + startIndex = index; + else if (colonCount == 1) + endIndex = index; + else + step = index; + } + + colonCount++; + + _currentIndex++; + + EatWhitespace(); + + start = _currentIndex; + end = null; + } + else if (!char.IsDigit(currentCharacter) && currentCharacter != '-') + { + throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter); + } + else + { + if (end != null) + throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter); + + _currentIndex++; + } + + } + + throw new JsonException("Path ended with open indexer."); + } + + private void EatWhitespace() + { + while (_currentIndex < _expression.Length) + { + if (_expression[_currentIndex] != ' ') + break; + + _currentIndex++; + } + } + + private PathFilter ParseQuery(char indexerCloseChar) + { + _currentIndex++; + EnsureLength("Path ended with open indexer."); + + if (_expression[_currentIndex] != '(') + throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]); + + _currentIndex++; + + QueryExpression expression = ParseExpression(); + + _currentIndex++; + EnsureLength("Path ended with open indexer."); + EatWhitespace(); + + if (_expression[_currentIndex] != indexerCloseChar) + throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]); + + return new QueryFilter + { + Expression = expression + }; + } + + private QueryExpression ParseExpression() + { + QueryExpression rootExpression = null; + CompositeExpression parentExpression = null; + + while (_currentIndex < _expression.Length) + { + EatWhitespace(); + + if (_expression[_currentIndex] != '@') + throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]); + + _currentIndex++; + + List expressionPath = new List(); + + if (ParsePath(expressionPath, _currentIndex, true)) + throw new JsonException("Path ended with open query."); + + EatWhitespace(); + EnsureLength("Path ended with open query."); + + QueryOperator op; + object value = null; + if (_expression[_currentIndex] == ')' + || _expression[_currentIndex] == '|' + || _expression[_currentIndex] == '&') + { + op = QueryOperator.Exists; + } + else + { + op = ParseOperator(); + + EatWhitespace(); + EnsureLength("Path ended with open query."); + + value = ParseValue(); + + EatWhitespace(); + EnsureLength("Path ended with open query."); + } + + BooleanQueryExpression booleanExpression = new BooleanQueryExpression + { + Path = expressionPath, + Operator = op, + Value = (op != QueryOperator.Exists) ? new JValue(value) : null + }; + + if (_expression[_currentIndex] == ')') + { + if (parentExpression != null) + { + parentExpression.Expressions.Add(booleanExpression); + return rootExpression; + } + + return booleanExpression; + } + if (_expression[_currentIndex] == '&' && Match("&&")) + { + if (parentExpression == null || parentExpression.Operator != QueryOperator.And) + { + CompositeExpression andExpression = new CompositeExpression { Operator = QueryOperator.And }; + + if (parentExpression != null) + parentExpression.Expressions.Add(andExpression); + + parentExpression = andExpression; + + if (rootExpression == null) + rootExpression = parentExpression; + } + + parentExpression.Expressions.Add(booleanExpression); + } + if (_expression[_currentIndex] == '|' && Match("||")) + { + if (parentExpression == null || parentExpression.Operator != QueryOperator.Or) + { + CompositeExpression orExpression = new CompositeExpression { Operator = QueryOperator.Or }; + + if (parentExpression != null) + parentExpression.Expressions.Add(orExpression); + + parentExpression = orExpression; + + if (rootExpression == null) + rootExpression = parentExpression; + } + + parentExpression.Expressions.Add(booleanExpression); + } + } + + throw new JsonException("Path ended with open query."); + } + + private object ParseValue() + { + char currentChar = _expression[_currentIndex]; + if (currentChar == '\'') + { + return ReadQuotedString(); + } + else if (char.IsDigit(currentChar) || currentChar == '-') + { + StringBuilder sb = new StringBuilder(); + sb.Append(currentChar); + + _currentIndex++; + while (_currentIndex < _expression.Length) + { + currentChar = _expression[_currentIndex]; + if (currentChar == ' ' || currentChar == ')') + { + string numberText = sb.ToString(); + + if (numberText.IndexOfAny(new char[] { '.', 'E', 'e' }) != -1) + { + double d; + if (double.TryParse(numberText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d)) + return d; + else + throw new JsonException("Could not read query value."); + } + else + { + long l; + if (long.TryParse(numberText, NumberStyles.Integer, CultureInfo.InvariantCulture, out l)) + return l; + else + throw new JsonException("Could not read query value."); + } + } + else + { + sb.Append(currentChar); + _currentIndex++; + } + } + } + else if (currentChar == 't') + { + if (Match("true")) + return true; + } + else if (currentChar == 'f') + { + if (Match("false")) + return false; + } + else if (currentChar == 'n') + { + if (Match("null")) + return null; + } + + throw new JsonException("Could not read query value."); + } + + private string ReadQuotedString() + { + StringBuilder sb = new StringBuilder(); + + _currentIndex++; + while (_currentIndex < _expression.Length) + { + char currentChar = _expression[_currentIndex]; + if (currentChar == '\\' && _currentIndex + 1 < _expression.Length) + { + _currentIndex++; + + if (_expression[_currentIndex] == '\'') + sb.Append('\''); + else if (_expression[_currentIndex] == '\\') + sb.Append('\\'); + else + throw new JsonException(@"Unknown escape chracter: \" + _expression[_currentIndex]); + + _currentIndex++; + } + else if (currentChar == '\'') + { + _currentIndex++; + { + return sb.ToString(); + } + } + else + { + _currentIndex++; + sb.Append(currentChar); + } + } + + throw new JsonException("Path ended with an open string."); + } + + private bool Match(string s) + { + int currentPosition = _currentIndex; + foreach (char c in s) + { + if (currentPosition < _expression.Length && _expression[currentPosition] == c) + currentPosition++; + else + return false; + } + + _currentIndex = currentPosition; + return true; + } + + private QueryOperator ParseOperator() + { + if (_currentIndex + 1 >= _expression.Length) + throw new JsonException("Path ended with open query."); + + if (Match("==")) + return QueryOperator.Equals; + if (Match("!=") || Match("<>")) + return QueryOperator.NotEquals; + if (Match("<=")) + return QueryOperator.LessThanOrEquals; + if (Match("<")) + return QueryOperator.LessThan; + if (Match(">=")) + return QueryOperator.GreaterThanOrEquals; + if (Match(">")) + return QueryOperator.GreaterThan; + + throw new JsonException("Could not read query operator."); + } + + private PathFilter ParseQuotedField(char indexerCloseChar) + { + List fields = null; + + while (_currentIndex < _expression.Length) + { + string field = ReadQuotedString(); + + EatWhitespace(); + EnsureLength("Path ended with open indexer."); + + if (_expression[_currentIndex] == indexerCloseChar) + { + if (fields != null) + { + fields.Add(field); + return new FieldMultipleFilter { Names = fields }; + } + else + { + return new FieldFilter { Name = field }; + } + } + else if (_expression[_currentIndex] == ',') + { + _currentIndex++; + EatWhitespace(); + + if (fields == null) + fields = new List(); + + fields.Add(field); + } + else + { + throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]); + } + } + + throw new JsonException("Path ended with open indexer."); + } + + private void EnsureLength(string message) + { + if (_currentIndex >= _expression.Length) + throw new JsonException(message); + } + + internal IEnumerable Evaluate(JToken t, bool errorWhenNoMatch) + { + return Evaluate(Filters, t, errorWhenNoMatch); + } + + internal static IEnumerable Evaluate(List filters, JToken t, bool errorWhenNoMatch) + { + IEnumerable current = new[] { t }; + foreach (PathFilter filter in filters) + { + current = filter.ExecuteFilter(current, errorWhenNoMatch); + } + + return current; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/PathFilter.cs b/Libs/JsonNet/Linq/JsonPath/PathFilter.cs new file mode 100644 index 0000000..90cc5bb --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/PathFilter.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal abstract class PathFilter + { + public abstract IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch); + + protected static JToken GetTokenIndex(JToken t, bool errorWhenNoMatch, int index) + { + JArray a = t as JArray; + JConstructor c = t as JConstructor; + + if (a != null) + { + if (a.Count <= index) + { + if (errorWhenNoMatch) + throw new JsonException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index)); + + return null; + } + + return a[index]; + } + else if (c != null) + { + if (c.Count <= index) + { + if (errorWhenNoMatch) + throw new JsonException("Index {0} outside the bounds of JConstructor.".FormatWith(CultureInfo.InvariantCulture, index)); + + return null; + } + + return c[index]; + } + else + { + if (errorWhenNoMatch) + throw new JsonException("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, t.GetType().Name)); + + return null; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/QueryExpression.cs b/Libs/JsonNet/Linq/JsonPath/QueryExpression.cs new file mode 100644 index 0000000..4ff4aab --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/QueryExpression.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal enum QueryOperator + { + None, + Equals, + NotEquals, + Exists, + LessThan, + LessThanOrEquals, + GreaterThan, + GreaterThanOrEquals, + And, + Or + } + + internal abstract class QueryExpression + { + public QueryOperator Operator { get; set; } + + public abstract bool IsMatch(JToken t); + } + + internal class CompositeExpression : QueryExpression + { + public List Expressions { get; set; } + + public CompositeExpression() + { + Expressions = new List(); + } + + public override bool IsMatch(JToken t) + { + switch (Operator) + { + case QueryOperator.And: + foreach (QueryExpression e in Expressions) + { + if (!e.IsMatch(t)) + return false; + } + return true; + case QueryOperator.Or: + foreach (QueryExpression e in Expressions) + { + if (e.IsMatch(t)) + return true; + } + return false; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + internal class BooleanQueryExpression : QueryExpression + { + public List Path { get; set; } + public JValue Value { get; set; } + + public override bool IsMatch(JToken t) + { + IEnumerable pathResult = JPath.Evaluate(Path, t, false); + + foreach (JToken r in pathResult) + { + JValue v = r as JValue; + switch (Operator) + { + case QueryOperator.Equals: + if (v != null && v.Equals(Value)) + return true; + break; + case QueryOperator.NotEquals: + if (v != null && !v.Equals(Value)) + return true; + break; + case QueryOperator.GreaterThan: + if (v != null && v.CompareTo(Value) > 0) + return true; + break; + case QueryOperator.GreaterThanOrEquals: + if (v != null && v.CompareTo(Value) >= 0) + return true; + break; + case QueryOperator.LessThan: + if (v != null && v.CompareTo(Value) < 0) + return true; + break; + case QueryOperator.LessThanOrEquals: + if (v != null && v.CompareTo(Value) <= 0) + return true; + break; + case QueryOperator.Exists: + return true; + default: + throw new ArgumentOutOfRangeException(); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/QueryFilter.cs b/Libs/JsonNet/Linq/JsonPath/QueryFilter.cs new file mode 100644 index 0000000..bbdfbcb --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/QueryFilter.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class QueryFilter : PathFilter + { + public QueryExpression Expression { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken t in current) + { + foreach (JToken v in t) + { + if (Expression.IsMatch(v)) + yield return v; + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/JsonPath/ScanFilter.cs b/Libs/JsonNet/Linq/JsonPath/ScanFilter.cs new file mode 100644 index 0000000..f07992f --- /dev/null +++ b/Libs/JsonNet/Linq/JsonPath/ScanFilter.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +namespace Newtonsoft.Json.Linq.JsonPath +{ + internal class ScanFilter : PathFilter + { + public string Name { get; set; } + + public override IEnumerable ExecuteFilter(IEnumerable current, bool errorWhenNoMatch) + { + foreach (JToken root in current) + { + if (Name == null) + yield return root; + + JToken value = root; + JToken container = root; + + while (true) + { + if (container != null && container.HasValues) + { + value = container.First; + } + else + { + while (value != null && value != root && value == value.Parent.Last) + { + value = value.Parent; + } + + if (value == null || value == root) + break; + + value = value.Next; + } + + JProperty e = value as JProperty; + if (e != null) + { + if (e.Name == Name) + yield return e.Value; + } + else + { + if (Name == null) + yield return value; + } + + container = value as JContainer; + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Linq/MergeArrayHandling.cs b/Libs/JsonNet/Linq/MergeArrayHandling.cs new file mode 100644 index 0000000..8055dc2 --- /dev/null +++ b/Libs/JsonNet/Linq/MergeArrayHandling.cs @@ -0,0 +1,20 @@ +namespace Newtonsoft.Json.Linq +{ + /// + /// Specifies how JSON arrays are merged together. + /// + public enum MergeArrayHandling + { + /// Concatenate arrays. + Concat, + + /// Union arrays, skipping items that already exist. + Union, + + /// Replace all array items. + Replace, + + /// Merge array items together, matched by index. + Merge + } +} \ No newline at end of file diff --git a/Libs/JsonNet/MemberSerialization.cs b/Libs/JsonNet/MemberSerialization.cs new file mode 100644 index 0000000..8ed5230 --- /dev/null +++ b/Libs/JsonNet/MemberSerialization.cs @@ -0,0 +1,58 @@ +#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.Runtime.Serialization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the member serialization options for the . + /// + public enum MemberSerialization + { +#pragma warning disable 1584,1711,1572,1581,1580,1574 + /// + /// All public members are serialized by default. Members can be excluded using or . + /// This is the default member serialization mode. + /// + OptOut, + + /// + /// Only members must be marked with or are serialized. + /// This member serialization mode can also be set by marking the class with . + /// + OptIn, + + /// + /// All public and private fields are serialized. Members can be excluded using or . + /// This member serialization mode can also be set by marking the class with + /// and setting IgnoreSerializableAttribute on to false. + /// + Fields +#pragma warning restore 1584,1711,1572,1581,1580,1574 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/MetadataPropertyHandling.cs b/Libs/JsonNet/MetadataPropertyHandling.cs new file mode 100644 index 0000000..5bdacd8 --- /dev/null +++ b/Libs/JsonNet/MetadataPropertyHandling.cs @@ -0,0 +1,52 @@ +#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 +{ + /// + /// Specifies metadata property handling options for the . + /// + public enum MetadataPropertyHandling + { + /// + /// Read metadata properties located at the start of a JSON object. + /// + Default = 0, + + /// + /// Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance. + /// + ReadAhead = 1, + + /// + /// Do not try to read metadata properties. + /// + Ignore = 2 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/MissingMemberHandling.cs b/Libs/JsonNet/MissingMemberHandling.cs new file mode 100644 index 0000000..4e35081 --- /dev/null +++ b/Libs/JsonNet/MissingMemberHandling.cs @@ -0,0 +1,47 @@ +#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 +{ + /// + /// Specifies missing member handling options for the . + /// + public enum MissingMemberHandling + { + /// + /// Ignore a missing member and do not attempt to deserialize it. + /// + Ignore = 0, + + /// + /// Throw a when a missing member is encountered during deserialization. + /// + Error = 1 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.Net20.csproj b/Libs/JsonNet/Newtonsoft.Json.Net20.csproj new file mode 100644 index 0000000..900041a --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.Net20.csproj @@ -0,0 +1,292 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + false + + + + + + + + + + + + + 3.5 + + + false + v2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\Net20\ + TRACE;DEBUG;NET20 + prompt + 4 + bin\Debug\Net20\Newtonsoft.Json.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Net20\ + TRACE;NET20 + prompt + 4 + bin\Release\Net20\Newtonsoft.Json.xml + Newtonsoft.Json.ruleset + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.Net35.csproj b/Libs/JsonNet/Newtonsoft.Json.Net35.csproj new file mode 100644 index 0000000..2181997 --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.Net35.csproj @@ -0,0 +1,308 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + false + + + + + + + + + + + + + 3.5 + + + false + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + Client + + + true + full + false + bin\Debug\Net35\ + TRACE;DEBUG;CODE_ANALYSIS;NET35 + prompt + 4 + bin\Debug\Net35\Newtonsoft.Json.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Net35\ + TRACE;NET35 + prompt + 4 + bin\Release\Net35\Newtonsoft.Json.xml + Newtonsoft.Json.ruleset + true + + + + + 3.5 + + + + 3.0 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.Net40.csproj b/Libs/JsonNet/Newtonsoft.Json.Net40.csproj new file mode 100644 index 0000000..7e56cb3 --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.Net40.csproj @@ -0,0 +1,309 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + false + + + + + + + + + + + + + 3.5 + + + false + v4.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + Client + + + true + full + false + bin\Debug\Net40\ + TRACE;DEBUG;NET40;CODE_ANALYSIS + prompt + 4 + bin\Debug\Net40\Newtonsoft.Json.xml + true + + + Newtonsoft.Json.ruleset + true + + + pdbonly + true + bin\Release\Net40\ + TRACE;NET40;CODE_ANALYSIS + prompt + 4 + bin\Release\Net40\Newtonsoft.Json.xml + Newtonsoft.Json.ruleset + true + true + + + + + 3.5 + + + + + 3.0 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.Portable.csproj b/Libs/JsonNet/Newtonsoft.Json.Portable.csproj new file mode 100644 index 0000000..17642ac --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.Portable.csproj @@ -0,0 +1,244 @@ + + + + Debug + AnyCPU + {959F7F85-C98B-4876-971A-9036224578E4} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + v4.5 + Profile259 + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 11.0 + + + true + full + false + bin\Debug\Portable\ + TRACE;DEBUG;PORTABLE + prompt + 4 + true + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Portable\ + TRACE;PORTABLE + prompt + 4 + bin\Release\Portable\Newtonsoft.Json.xml + true + Newtonsoft.Json.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.Portable40.csproj b/Libs/JsonNet/Newtonsoft.Json.Portable40.csproj new file mode 100644 index 0000000..164b407 --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.Portable40.csproj @@ -0,0 +1,248 @@ + + + + Debug + AnyCPU + {959F7F85-C98B-4876-971A-9036224578E4} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + v4.0 + Profile328 + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + + + + + 4.0 + + + true + full + false + bin\Debug\Portable40\ + TRACE;DEBUG;CODE_ANALYSIS;PORTABLE40 + prompt + 4 + true + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Portable40\ + TRACE;CODE_ANALYSIS;PORTABLE40 + prompt + 4 + bin\Release\Portable40\Newtonsoft.Json.xml + true + Newtonsoft.Json.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.WinRT.csproj b/Libs/JsonNet/Newtonsoft.Json.WinRT.csproj new file mode 100644 index 0000000..d54a54f --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.WinRT.csproj @@ -0,0 +1,317 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {263136A2-B89B-424E-A87A-F988CAA8D032} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + en-US + 512 + {BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\WinRT\ + DEBUG;TRACE;NETFX_CORE + prompt + 3 + Newtonsoft.Json.ruleset + true + + + pdbonly + true + bin\Release\WinRT\ + TRACE;NETFX_CORE + prompt + 4 + bin\Release\WinRT\Newtonsoft.Json.xml + true + Newtonsoft.Json.ruleset + + + true + bin\ARM\Debug\ + DEBUG;TRACE;NETFX_CORE + ;2008 + full + ARM + false + prompt + ExpressRules.ruleset + true + + + bin\ARM\Release\ + TRACE;NETFX_CORE + true + ;2008 + pdbonly + ARM + false + prompt + ExpressRules.ruleset + true + + + true + bin\x64\Debug\ + DEBUG;TRACE;NETFX_CORE + ;2008 + full + x64 + false + prompt + ExpressRules.ruleset + true + + + bin\x64\Release\ + TRACE;NETFX_CORE + true + ;2008 + pdbonly + x64 + false + prompt + ExpressRules.ruleset + true + + + true + bin\x86\Debug\ + DEBUG;TRACE;NETFX_CORE + ;2008 + full + x86 + false + prompt + ExpressRules.ruleset + true + + + bin\x86\Release\ + TRACE;NETFX_CORE + true + ;2008 + pdbonly + x86 + false + prompt + ExpressRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12.0 + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.csproj b/Libs/JsonNet/Newtonsoft.Json.csproj new file mode 100644 index 0000000..31aa58d --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.csproj @@ -0,0 +1,248 @@ + + + + + Debug + AnyCPU + {EEEA67F9-9089-481D-A4DC-0966DEC9FD7D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + v4.5 + 512 + + + true + full + false + bin\Debug\Net45\ + TRACE;DEBUG;CODE_ANALYSIS + prompt + 4 + bin\Debug\Net45\Newtonsoft.Json.xml + true + Newtonsoft.Json.ruleset + true + + + pdbonly + true + bin\Release\Net45\ + TRACE;CODE_ANALYSIS + prompt + 4 + bin\Release\Net45\Newtonsoft.Json.xml + true + Newtonsoft.Json.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/Newtonsoft.Json.ruleset b/Libs/JsonNet/Newtonsoft.Json.ruleset new file mode 100644 index 0000000..17596c2 --- /dev/null +++ b/Libs/JsonNet/Newtonsoft.Json.ruleset @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Libs/JsonNet/NullValueHandling.cs b/Libs/JsonNet/NullValueHandling.cs new file mode 100644 index 0000000..8a88c16 --- /dev/null +++ b/Libs/JsonNet/NullValueHandling.cs @@ -0,0 +1,47 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies null value handling options for the . + /// + /// + /// + /// + /// + public enum NullValueHandling + { + /// + /// Include null values when serializing and deserializing objects. + /// + Include = 0, + + /// + /// Ignore null values when serializing and deserializing objects. + /// + Ignore = 1 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/ObjectCreationHandling.cs b/Libs/JsonNet/ObjectCreationHandling.cs new file mode 100644 index 0000000..cc53b14 --- /dev/null +++ b/Libs/JsonNet/ObjectCreationHandling.cs @@ -0,0 +1,48 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how object creation is handled by the . + /// + public enum ObjectCreationHandling + { + /// + /// Reuse existing objects, create new objects when needed. + /// + Auto = 0, + + /// + /// Only reuse existing objects. + /// + Reuse = 1, + + /// + /// Always create new objects. + /// + Replace = 2 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/PreserveReferencesHandling.cs b/Libs/JsonNet/PreserveReferencesHandling.cs new file mode 100644 index 0000000..61fcbd4 --- /dev/null +++ b/Libs/JsonNet/PreserveReferencesHandling.cs @@ -0,0 +1,62 @@ +#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 +{ + /// + /// Specifies reference handling options for the . + /// Note that references cannot be preserved when a value is set via a non-default constructor such as types that implement ISerializable. + /// + /// + /// + /// + [Flags] + public enum PreserveReferencesHandling + { + /// + /// Do not preserve references when serializing types. + /// + None = 0, + + /// + /// Preserve references when serializing into a JSON object structure. + /// + Objects = 1, + + /// + /// Preserve references when serializing into a JSON array structure. + /// + Arrays = 2, + + /// + /// Preserve references when serializing. + /// + All = Objects | Arrays + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Properties/AssemblyInfo.cs b/Libs/JsonNet/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5246530 --- /dev/null +++ b/Libs/JsonNet/Properties/AssemblyInfo.cs @@ -0,0 +1,96 @@ +#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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +#if PORTABLE40 +[assembly: AssemblyTitle("Json.NET Portable .NET 4.0")] +#elif PORTABLE +[assembly: AssemblyTitle("Json.NET Portable")] +#elif NETFX_CORE +[assembly: AssemblyTitle("Json.NET WinRT")] +#elif NET20 +[assembly: AssemblyTitle("Json.NET .NET 2.0")] +[assembly: AllowPartiallyTrustedCallers] +#elif NET35 +[assembly: AssemblyTitle("Json.NET .NET 3.5")] +[assembly: AllowPartiallyTrustedCallers] +#elif NET40 +[assembly: AssemblyTitle("Json.NET .NET 4.0")] +[assembly: AllowPartiallyTrustedCallers] +#else +[assembly: AssemblyTitle("Json.NET")] +[assembly: AllowPartiallyTrustedCallers] +#endif + +#if !SIGNED + +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests")] +#else +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#endif + +[assembly: InternalsVisibleTo("Newtonsoft.Json.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cbd8d53b9d7de30f1f1278f636ec462cf9c254991291e66ebb157a885638a517887633b898ccbcf0d5c5ff7be85a6abe9e765d0ac7cd33c68dac67e7e64530e8222101109f154ab14a941c490ac155cd1d4fcba0fabb49016b4ef28593b015cab5937da31172f03f67d09edda404b88a60023f062ae71d0b2e4438b74cc11dc9")] +[assembly: AssemblyDescription("Json.NET is a popular high-performance JSON framework for .NET")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Newtonsoft")] +[assembly: AssemblyProduct("Json.NET")] +[assembly: AssemblyCopyright("Copyright James Newton-King 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +#if !(PORTABLE40 || PORTABLE) +// Setting ComVisible to false makes the types in this assembly not visible +// to COM componenets. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("9ca358aa-317b-4925-8ada-4a29e943a363")] +#endif + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("6.0.0.0")] +[assembly: AssemblyFileVersion("6.0.6.17929")] +[assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo("VKSharp")] \ No newline at end of file diff --git a/Libs/JsonNet/ReferenceLoopHandling.cs b/Libs/JsonNet/ReferenceLoopHandling.cs new file mode 100644 index 0000000..bfa5133 --- /dev/null +++ b/Libs/JsonNet/ReferenceLoopHandling.cs @@ -0,0 +1,52 @@ +#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 +{ + /// + /// Specifies reference loop handling options for the . + /// + public enum ReferenceLoopHandling + { + /// + /// Throw a when a loop is encountered. + /// + Error = 0, + + /// + /// Ignore loop references and do not serialize. + /// + Ignore = 1, + + /// + /// Serialize loop references. + /// + Serialize = 2 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Required.cs b/Libs/JsonNet/Required.cs new file mode 100644 index 0000000..889aa6d --- /dev/null +++ b/Libs/JsonNet/Required.cs @@ -0,0 +1,48 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Indicating whether a property is required. + /// + public enum Required + { + /// + /// The property is not required. The default state. + /// + Default, + + /// + /// The property must be defined in JSON but can be a null value. + /// + AllowNull, + + /// + /// The property must be defined in JSON and cannot be a null value. + /// + Always + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/Extensions.cs b/Libs/JsonNet/Schema/Extensions.cs new file mode 100644 index 0000000..2532dff --- /dev/null +++ b/Libs/JsonNet/Schema/Extensions.cs @@ -0,0 +1,104 @@ +#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.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Contains the JSON schema extension methods. + /// + public static class Extensions + { + /// + /// Determines whether the is valid. + /// + /// The source to test. + /// The schema to test with. + /// + /// true if the specified is valid; otherwise, false. + /// + public static bool IsValid(this JToken source, JsonSchema schema) + { + bool valid = true; + source.Validate(schema, (sender, args) => { valid = false; }); + return valid; + } + + /// + /// Determines whether the is valid. + /// + /// The source to test. + /// The schema to test with. + /// When this method returns, contains any error messages generated while validating. + /// + /// true if the specified is valid; otherwise, false. + /// + public static bool IsValid(this JToken source, JsonSchema schema, out IList errorMessages) + { + IList errors = new List(); + + source.Validate(schema, (sender, args) => errors.Add(args.Message)); + + errorMessages = errors; + return (errorMessages.Count == 0); + } + + /// + /// Validates the specified . + /// + /// The source to test. + /// The schema to test with. + public static void Validate(this JToken source, JsonSchema schema) + { + source.Validate(schema, null); + } + + /// + /// Validates the specified . + /// + /// The source to test. + /// The schema to test with. + /// The validation event handler. + public static void Validate(this JToken source, JsonSchema schema, ValidationEventHandler validationEventHandler) + { + ValidationUtils.ArgumentNotNull(source, "source"); + ValidationUtils.ArgumentNotNull(schema, "schema"); + + using (JsonValidatingReader reader = new JsonValidatingReader(source.CreateReader())) + { + reader.Schema = schema; + if (validationEventHandler != null) + reader.ValidationEventHandler += validationEventHandler; + + while (reader.Read()) + { + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchema.cs b/Libs/JsonNet/Schema/JsonSchema.cs new file mode 100644 index 0000000..25ad8c6 --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchema.cs @@ -0,0 +1,350 @@ +#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.IO; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Schema +{ + /// + /// An in-memory representation of a JSON Schema. + /// + public class JsonSchema + { + /// + /// Gets or sets the id. + /// + public string Id { get; set; } + + /// + /// Gets or sets the title. + /// + public string Title { get; set; } + + /// + /// Gets or sets whether the object is required. + /// + public bool? Required { get; set; } + + /// + /// Gets or sets whether the object is read only. + /// + public bool? ReadOnly { get; set; } + + /// + /// Gets or sets whether the object is visible to users. + /// + public bool? Hidden { get; set; } + + /// + /// Gets or sets whether the object is transient. + /// + public bool? Transient { get; set; } + + /// + /// Gets or sets the description of the object. + /// + public string Description { get; set; } + + /// + /// Gets or sets the types of values allowed by the object. + /// + /// The type. + public JsonSchemaType? Type { get; set; } + + /// + /// Gets or sets the pattern. + /// + /// The pattern. + public string Pattern { get; set; } + + /// + /// Gets or sets the minimum length. + /// + /// The minimum length. + public int? MinimumLength { get; set; } + + /// + /// Gets or sets the maximum length. + /// + /// The maximum length. + public int? MaximumLength { get; set; } + + /// + /// Gets or sets a number that the value should be divisble by. + /// + /// A number that the value should be divisble by. + public double? DivisibleBy { get; set; } + + /// + /// Gets or sets the minimum. + /// + /// The minimum. + public double? Minimum { get; set; } + + /// + /// Gets or sets the maximum. + /// + /// The maximum. + public double? Maximum { get; set; } + + /// + /// Gets or sets a flag indicating whether the value can not equal the number defined by the "minimum" attribute. + /// + /// A flag indicating whether the value can not equal the number defined by the "minimum" attribute. + public bool? ExclusiveMinimum { get; set; } + + /// + /// Gets or sets a flag indicating whether the value can not equal the number defined by the "maximum" attribute. + /// + /// A flag indicating whether the value can not equal the number defined by the "maximum" attribute. + public bool? ExclusiveMaximum { get; set; } + + /// + /// Gets or sets the minimum number of items. + /// + /// The minimum number of items. + public int? MinimumItems { get; set; } + + /// + /// Gets or sets the maximum number of items. + /// + /// The maximum number of items. + public int? MaximumItems { get; set; } + + /// + /// Gets or sets the of items. + /// + /// The of items. + public IList Items { get; set; } + + /// + /// Gets or sets a value indicating whether items in an array are validated using the instance at their array position from . + /// + /// + /// true if items are validated using their array position; otherwise, false. + /// + public bool PositionalItemsValidation { get; set; } + + /// + /// Gets or sets the of additional items. + /// + /// The of additional items. + public JsonSchema AdditionalItems { get; set; } + + /// + /// Gets or sets a value indicating whether additional items are allowed. + /// + /// + /// true if additional items are allowed; otherwise, false. + /// + public bool AllowAdditionalItems { get; set; } + + /// + /// Gets or sets whether the array items must be unique. + /// + public bool UniqueItems { get; set; } + + /// + /// Gets or sets the of properties. + /// + /// The of properties. + public IDictionary Properties { get; set; } + + /// + /// Gets or sets the of additional properties. + /// + /// The of additional properties. + public JsonSchema AdditionalProperties { get; set; } + + /// + /// Gets or sets the pattern properties. + /// + /// The pattern properties. + public IDictionary PatternProperties { get; set; } + + /// + /// Gets or sets a value indicating whether additional properties are allowed. + /// + /// + /// true if additional properties are allowed; otherwise, false. + /// + public bool AllowAdditionalProperties { get; set; } + + /// + /// Gets or sets the required property if this property is present. + /// + /// The required property if this property is present. + public string Requires { get; set; } + + /// + /// Gets or sets the a collection of valid enum values allowed. + /// + /// A collection of valid enum values allowed. + public IList Enum { get; set; } + + /// + /// Gets or sets disallowed types. + /// + /// The disallow types. + public JsonSchemaType? Disallow { get; set; } + + /// + /// Gets or sets the default value. + /// + /// The default value. + public JToken Default { get; set; } + + /// + /// Gets or sets the collection of that this schema extends. + /// + /// The collection of that this schema extends. + public IList Extends { get; set; } + + /// + /// Gets or sets the format. + /// + /// The format. + public string Format { get; set; } + + internal string Location { get; set; } + + private readonly string _internalId = Guid.NewGuid().ToString("N"); + + internal string InternalId + { + get { return _internalId; } + } + + // if this is set then this schema instance is just a deferred reference + // and will be replaced when the schema reference is resolved + internal string DeferredReference { get; set; } + internal bool ReferencesResolved { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchema() + { + AllowAdditionalProperties = true; + AllowAdditionalItems = true; + } + + /// + /// Reads a from the specified . + /// + /// The containing the JSON Schema to read. + /// The object representing the JSON Schema. + public static JsonSchema Read(JsonReader reader) + { + return Read(reader, new JsonSchemaResolver()); + } + + /// + /// Reads a from the specified . + /// + /// The containing the JSON Schema to read. + /// The to use when resolving schema references. + /// The object representing the JSON Schema. + public static JsonSchema Read(JsonReader reader, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + JsonSchemaBuilder builder = new JsonSchemaBuilder(resolver); + return builder.Read(reader); + } + + /// + /// Load a from a string that contains schema JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static JsonSchema Parse(string json) + { + return Parse(json, new JsonSchemaResolver()); + } + + /// + /// Parses the specified json. + /// + /// The json. + /// The resolver. + /// A populated from the string that contains JSON. + public static JsonSchema Parse(string json, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(json, "json"); + + using (JsonReader reader = new JsonTextReader(new StringReader(json))) + { + return Read(reader, resolver); + } + } + + /// + /// Writes this schema to a . + /// + /// A into which this method will write. + public void WriteTo(JsonWriter writer) + { + WriteTo(writer, new JsonSchemaResolver()); + } + + /// + /// Writes this schema to a using the specified . + /// + /// A into which this method will write. + /// The resolver used. + public void WriteTo(JsonWriter writer, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + JsonSchemaWriter schemaWriter = new JsonSchemaWriter(writer, resolver); + schemaWriter.WriteSchema(this); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + StringWriter writer = new StringWriter(CultureInfo.InvariantCulture); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + WriteTo(jsonWriter); + + return writer.ToString(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaBuilder.cs b/Libs/JsonNet/Schema/JsonSchemaBuilder.cs new file mode 100644 index 0000000..72973e7 --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaBuilder.cs @@ -0,0 +1,448 @@ +#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 Newtonsoft.Json.Serialization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using System.Globalization; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaBuilder + { + private readonly IList _stack; + private readonly JsonSchemaResolver _resolver; + private readonly IDictionary _documentSchemas; + private JsonSchema _currentSchema; + private JObject _rootSchema; + + public JsonSchemaBuilder(JsonSchemaResolver resolver) + { + _stack = new List(); + _documentSchemas = new Dictionary(); + _resolver = resolver; + } + + private void Push(JsonSchema value) + { + _currentSchema = value; + _stack.Add(value); + _resolver.LoadedSchemas.Add(value); + _documentSchemas.Add(value.Location, value); + } + + private JsonSchema Pop() + { + JsonSchema poppedSchema = _currentSchema; + _stack.RemoveAt(_stack.Count - 1); + _currentSchema = _stack.LastOrDefault(); + + return poppedSchema; + } + + private JsonSchema CurrentSchema + { + get { return _currentSchema; } + } + + internal JsonSchema Read(JsonReader reader) + { + JToken schemaToken = JToken.ReadFrom(reader); + + _rootSchema = schemaToken as JObject; + + JsonSchema schema = BuildSchema(schemaToken); + + ResolveReferences(schema); + + return schema; + } + + private string UnescapeReference(string reference) + { + return Uri.UnescapeDataString(reference).Replace("~1", "/").Replace("~0", "~"); + } + + private JsonSchema ResolveReferences(JsonSchema schema) + { + if (schema.DeferredReference != null) + { + string reference = schema.DeferredReference; + + bool locationReference = (reference.StartsWith("#", StringComparison.Ordinal)); + if (locationReference) + reference = UnescapeReference(reference); + + JsonSchema resolvedSchema = _resolver.GetSchema(reference); + + if (resolvedSchema == null) + { + if (locationReference) + { + string[] escapedParts = schema.DeferredReference.TrimStart('#').Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + JToken currentToken = _rootSchema; + foreach (string escapedPart in escapedParts) + { + string part = UnescapeReference(escapedPart); + + if (currentToken.Type == JTokenType.Object) + { + currentToken = currentToken[part]; + } + else if (currentToken.Type == JTokenType.Array || currentToken.Type == JTokenType.Constructor) + { + int index; + if (int.TryParse(part, out index) && index >= 0 && index < currentToken.Count()) + currentToken = currentToken[index]; + else + currentToken = null; + } + + if (currentToken == null) + break; + } + + if (currentToken != null) + resolvedSchema = BuildSchema(currentToken); + } + + if (resolvedSchema == null) + throw new JsonException("Could not resolve schema reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, schema.DeferredReference)); + } + + schema = resolvedSchema; + } + + if (schema.ReferencesResolved) + return schema; + + schema.ReferencesResolved = true; + + if (schema.Extends != null) + { + for (int i = 0; i < schema.Extends.Count; i++) + { + schema.Extends[i] = ResolveReferences(schema.Extends[i]); + } + } + + if (schema.Items != null) + { + for (int i = 0; i < schema.Items.Count; i++) + { + schema.Items[i] = ResolveReferences(schema.Items[i]); + } + } + + if (schema.AdditionalItems != null) + schema.AdditionalItems = ResolveReferences(schema.AdditionalItems); + + if (schema.PatternProperties != null) + { + foreach (KeyValuePair patternProperty in schema.PatternProperties.ToList()) + { + schema.PatternProperties[patternProperty.Key] = ResolveReferences(patternProperty.Value); + } + } + + if (schema.Properties != null) + { + foreach (KeyValuePair property in schema.Properties.ToList()) + { + schema.Properties[property.Key] = ResolveReferences(property.Value); + } + } + + if (schema.AdditionalProperties != null) + schema.AdditionalProperties = ResolveReferences(schema.AdditionalProperties); + + return schema; + } + + private JsonSchema BuildSchema(JToken token) + { + JObject schemaObject = token as JObject; + if (schemaObject == null) + throw JsonException.Create(token, token.Path, "Expected object while parsing schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + JToken referenceToken; + if (schemaObject.TryGetValue(JsonTypeReflector.RefPropertyName, out referenceToken)) + { + JsonSchema deferredSchema = new JsonSchema(); + deferredSchema.DeferredReference = (string)referenceToken; + + return deferredSchema; + } + + string location = token.Path.Replace(".", "/").Replace("[", "/").Replace("]", string.Empty); + if (!string.IsNullOrEmpty(location)) + location = "/" + location; + location = "#" + location; + + JsonSchema existingSchema; + if (_documentSchemas.TryGetValue(location, out existingSchema)) + return existingSchema; + + Push(new JsonSchema { Location = location }); + + ProcessSchemaProperties(schemaObject); + + return Pop(); + } + + private void ProcessSchemaProperties(JObject schemaObject) + { + foreach (KeyValuePair property in schemaObject) + { + switch (property.Key) + { + case JsonSchemaConstants.TypePropertyName: + CurrentSchema.Type = ProcessType(property.Value); + break; + case JsonSchemaConstants.IdPropertyName: + CurrentSchema.Id = (string)property.Value; + break; + case JsonSchemaConstants.TitlePropertyName: + CurrentSchema.Title = (string)property.Value; + break; + case JsonSchemaConstants.DescriptionPropertyName: + CurrentSchema.Description = (string)property.Value; + break; + case JsonSchemaConstants.PropertiesPropertyName: + CurrentSchema.Properties = ProcessProperties(property.Value); + break; + case JsonSchemaConstants.ItemsPropertyName: + ProcessItems(property.Value); + break; + case JsonSchemaConstants.AdditionalPropertiesPropertyName: + ProcessAdditionalProperties(property.Value); + break; + case JsonSchemaConstants.AdditionalItemsPropertyName: + ProcessAdditionalItems(property.Value); + break; + case JsonSchemaConstants.PatternPropertiesPropertyName: + CurrentSchema.PatternProperties = ProcessProperties(property.Value); + break; + case JsonSchemaConstants.RequiredPropertyName: + CurrentSchema.Required = (bool)property.Value; + break; + case JsonSchemaConstants.RequiresPropertyName: + CurrentSchema.Requires = (string)property.Value; + break; + case JsonSchemaConstants.MinimumPropertyName: + CurrentSchema.Minimum = (double)property.Value; + break; + case JsonSchemaConstants.MaximumPropertyName: + CurrentSchema.Maximum = (double)property.Value; + break; + case JsonSchemaConstants.ExclusiveMinimumPropertyName: + CurrentSchema.ExclusiveMinimum = (bool)property.Value; + break; + case JsonSchemaConstants.ExclusiveMaximumPropertyName: + CurrentSchema.ExclusiveMaximum = (bool)property.Value; + break; + case JsonSchemaConstants.MaximumLengthPropertyName: + CurrentSchema.MaximumLength = (int)property.Value; + break; + case JsonSchemaConstants.MinimumLengthPropertyName: + CurrentSchema.MinimumLength = (int)property.Value; + break; + case JsonSchemaConstants.MaximumItemsPropertyName: + CurrentSchema.MaximumItems = (int)property.Value; + break; + case JsonSchemaConstants.MinimumItemsPropertyName: + CurrentSchema.MinimumItems = (int)property.Value; + break; + case JsonSchemaConstants.DivisibleByPropertyName: + CurrentSchema.DivisibleBy = (double)property.Value; + break; + case JsonSchemaConstants.DisallowPropertyName: + CurrentSchema.Disallow = ProcessType(property.Value); + break; + case JsonSchemaConstants.DefaultPropertyName: + CurrentSchema.Default = property.Value.DeepClone(); + break; + case JsonSchemaConstants.HiddenPropertyName: + CurrentSchema.Hidden = (bool)property.Value; + break; + case JsonSchemaConstants.ReadOnlyPropertyName: + CurrentSchema.ReadOnly = (bool)property.Value; + break; + case JsonSchemaConstants.FormatPropertyName: + CurrentSchema.Format = (string)property.Value; + break; + case JsonSchemaConstants.PatternPropertyName: + CurrentSchema.Pattern = (string)property.Value; + break; + case JsonSchemaConstants.EnumPropertyName: + ProcessEnum(property.Value); + break; + case JsonSchemaConstants.ExtendsPropertyName: + ProcessExtends(property.Value); + break; + case JsonSchemaConstants.UniqueItemsPropertyName: + CurrentSchema.UniqueItems = (bool)property.Value; + break; + } + } + } + + private void ProcessExtends(JToken token) + { + IList schemas = new List(); + + if (token.Type == JTokenType.Array) + { + foreach (JToken schemaObject in token) + { + schemas.Add(BuildSchema(schemaObject)); + } + } + else + { + JsonSchema schema = BuildSchema(token); + if (schema != null) + schemas.Add(schema); + } + + if (schemas.Count > 0) + CurrentSchema.Extends = schemas; + } + + private void ProcessEnum(JToken token) + { + if (token.Type != JTokenType.Array) + throw JsonException.Create(token, token.Path, "Expected Array token while parsing enum values, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + CurrentSchema.Enum = new List(); + + foreach (JToken enumValue in token) + { + CurrentSchema.Enum.Add(enumValue.DeepClone()); + } + } + + private void ProcessAdditionalProperties(JToken token) + { + if (token.Type == JTokenType.Boolean) + CurrentSchema.AllowAdditionalProperties = (bool)token; + else + CurrentSchema.AdditionalProperties = BuildSchema(token); + } + + private void ProcessAdditionalItems(JToken token) + { + if (token.Type == JTokenType.Boolean) + CurrentSchema.AllowAdditionalItems = (bool)token; + else + CurrentSchema.AdditionalItems = BuildSchema(token); + } + + private IDictionary ProcessProperties(JToken token) + { + IDictionary properties = new Dictionary(); + + if (token.Type != JTokenType.Object) + throw JsonException.Create(token, token.Path, "Expected Object token while parsing schema properties, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + foreach (JProperty propertyToken in token) + { + if (properties.ContainsKey(propertyToken.Name)) + throw new JsonException("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, propertyToken.Name)); + + properties.Add(propertyToken.Name, BuildSchema(propertyToken.Value)); + } + + return properties; + } + + private void ProcessItems(JToken token) + { + CurrentSchema.Items = new List(); + + switch (token.Type) + { + case JTokenType.Object: + CurrentSchema.Items.Add(BuildSchema(token)); + CurrentSchema.PositionalItemsValidation = false; + break; + case JTokenType.Array: + CurrentSchema.PositionalItemsValidation = true; + foreach (JToken schemaToken in token) + { + CurrentSchema.Items.Add(BuildSchema(schemaToken)); + } + break; + default: + throw JsonException.Create(token, token.Path, "Expected array or JSON schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + } + } + + private JsonSchemaType? ProcessType(JToken token) + { + switch (token.Type) + { + case JTokenType.Array: + // ensure type is in blank state before ORing values + JsonSchemaType? type = JsonSchemaType.None; + + foreach (JToken typeToken in token) + { + if (typeToken.Type != JTokenType.String) + throw JsonException.Create(typeToken, typeToken.Path, "Exception JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + type = type | MapType((string)typeToken); + } + + return type; + case JTokenType.String: + return MapType((string)token); + default: + throw JsonException.Create(token, token.Path, "Expected array or JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + } + } + + internal static JsonSchemaType MapType(string type) + { + JsonSchemaType mappedType; + if (!JsonSchemaConstants.JsonSchemaTypeMapping.TryGetValue(type, out mappedType)) + throw new JsonException("Invalid JSON schema type: {0}".FormatWith(CultureInfo.InvariantCulture, type)); + + return mappedType; + } + + internal static string MapType(JsonSchemaType type) + { + return JsonSchemaConstants.JsonSchemaTypeMapping.Single(kv => kv.Value == type).Key; + } + } +} diff --git a/Libs/JsonNet/Schema/JsonSchemaConstants.cs b/Libs/JsonNet/Schema/JsonSchemaConstants.cs new file mode 100644 index 0000000..26b824d --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaConstants.cs @@ -0,0 +1,78 @@ +#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.Collections.Generic; + +namespace Newtonsoft.Json.Schema +{ + internal static class JsonSchemaConstants + { + public const string TypePropertyName = "type"; + public const string PropertiesPropertyName = "properties"; + public const string ItemsPropertyName = "items"; + public const string AdditionalItemsPropertyName = "additionalItems"; + public const string RequiredPropertyName = "required"; + public const string PatternPropertiesPropertyName = "patternProperties"; + public const string AdditionalPropertiesPropertyName = "additionalProperties"; + public const string RequiresPropertyName = "requires"; + public const string MinimumPropertyName = "minimum"; + public const string MaximumPropertyName = "maximum"; + public const string ExclusiveMinimumPropertyName = "exclusiveMinimum"; + public const string ExclusiveMaximumPropertyName = "exclusiveMaximum"; + public const string MinimumItemsPropertyName = "minItems"; + public const string MaximumItemsPropertyName = "maxItems"; + public const string PatternPropertyName = "pattern"; + public const string MaximumLengthPropertyName = "maxLength"; + public const string MinimumLengthPropertyName = "minLength"; + public const string EnumPropertyName = "enum"; + public const string ReadOnlyPropertyName = "readonly"; + public const string TitlePropertyName = "title"; + public const string DescriptionPropertyName = "description"; + public const string FormatPropertyName = "format"; + public const string DefaultPropertyName = "default"; + public const string TransientPropertyName = "transient"; + public const string DivisibleByPropertyName = "divisibleBy"; + public const string HiddenPropertyName = "hidden"; + public const string DisallowPropertyName = "disallow"; + public const string ExtendsPropertyName = "extends"; + public const string IdPropertyName = "id"; + public const string UniqueItemsPropertyName = "uniqueItems"; + + public const string OptionValuePropertyName = "value"; + public const string OptionLabelPropertyName = "label"; + + public static readonly IDictionary JsonSchemaTypeMapping = new Dictionary + { + { "string", JsonSchemaType.String }, + { "object", JsonSchemaType.Object }, + { "integer", JsonSchemaType.Integer }, + { "number", JsonSchemaType.Float }, + { "null", JsonSchemaType.Null }, + { "boolean", JsonSchemaType.Boolean }, + { "array", JsonSchemaType.Array }, + { "any", JsonSchemaType.Any } + }; + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaException.cs b/Libs/JsonNet/Schema/JsonSchemaException.cs new file mode 100644 index 0000000..5cc0f4f --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaException.cs @@ -0,0 +1,108 @@ +#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.Runtime.Serialization; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Returns detailed information about the schema exception. + /// +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + [Serializable] +#endif + public class JsonSchemaException : JsonException + { + /// + /// Gets the line number indicating where the error occurred. + /// + /// The line number indicating where the error occurred. + public int LineNumber { get; private set; } + + + /// + /// Gets the line position indicating where the error occurred. + /// + /// The line position indicating where the error occurred. + public int LinePosition { get; private set; } + + /// + /// Gets the path to the JSON where the error occurred. + /// + /// The path to the JSON where the error occurred. + public string Path { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchemaException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonSchemaException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonSchemaException(string message, Exception innerException) + : base(message, innerException) + { + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// The class name is null or is zero (0). + public JsonSchemaException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + internal JsonSchemaException(string message, Exception innerException, string path, int lineNumber, int linePosition) + : base(message, innerException) + { + Path = path; + LineNumber = lineNumber; + LinePosition = linePosition; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaGenerator.cs b/Libs/JsonNet/Schema/JsonSchemaGenerator.cs new file mode 100644 index 0000000..fef3fca --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaGenerator.cs @@ -0,0 +1,473 @@ +#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.Globalization; +using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Serialization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Schema +{ + /// + /// Generates a from a specified . + /// + public class JsonSchemaGenerator + { + /// + /// Gets or sets how undefined schemas are handled by the serializer. + /// + public UndefinedSchemaIdHandling UndefinedSchemaIdHandling { get; set; } + + private IContractResolver _contractResolver; + + /// + /// Gets or sets the contract resolver. + /// + /// The contract resolver. + public IContractResolver ContractResolver + { + get + { + if (_contractResolver == null) + return DefaultContractResolver.Instance; + + return _contractResolver; + } + set { _contractResolver = value; } + } + + private class TypeSchema + { + public Type Type { get; private set; } + public JsonSchema Schema { get; private set; } + + public TypeSchema(Type type, JsonSchema schema) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(schema, "schema"); + + Type = type; + Schema = schema; + } + } + + private JsonSchemaResolver _resolver; + private readonly IList _stack = new List(); + private JsonSchema _currentSchema; + + private JsonSchema CurrentSchema + { + get { return _currentSchema; } + } + + private void Push(TypeSchema typeSchema) + { + _currentSchema = typeSchema.Schema; + _stack.Add(typeSchema); + _resolver.LoadedSchemas.Add(typeSchema.Schema); + } + + private TypeSchema Pop() + { + TypeSchema popped = _stack[_stack.Count - 1]; + _stack.RemoveAt(_stack.Count - 1); + TypeSchema newValue = _stack.LastOrDefault(); + if (newValue != null) + { + _currentSchema = newValue.Schema; + } + else + { + _currentSchema = null; + } + + return popped; + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// A generated from the specified type. + public JsonSchema Generate(Type type) + { + return Generate(type, new JsonSchemaResolver(), false); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// The used to resolve schema references. + /// A generated from the specified type. + public JsonSchema Generate(Type type, JsonSchemaResolver resolver) + { + return Generate(type, resolver, false); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// Specify whether the generated root will be nullable. + /// A generated from the specified type. + public JsonSchema Generate(Type type, bool rootSchemaNullable) + { + return Generate(type, new JsonSchemaResolver(), rootSchemaNullable); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// The used to resolve schema references. + /// Specify whether the generated root will be nullable. + /// A generated from the specified type. + public JsonSchema Generate(Type type, JsonSchemaResolver resolver, bool rootSchemaNullable) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + _resolver = resolver; + + return GenerateInternal(type, (!rootSchemaNullable) ? Required.Always : Required.Default, false); + } + + private string GetTitle(Type type) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Title)) + return containerAttribute.Title; + + return null; + } + + private string GetDescription(Type type) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Description)) + return containerAttribute.Description; + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + DescriptionAttribute descriptionAttribute = ReflectionUtils.GetAttribute(type); + if (descriptionAttribute != null) + return descriptionAttribute.Description; +#endif + + return null; + } + + private string GetTypeId(Type type, bool explicitOnly) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Id)) + return containerAttribute.Id; + + if (explicitOnly) + return null; + + switch (UndefinedSchemaIdHandling) + { + case UndefinedSchemaIdHandling.UseTypeName: + return type.FullName; + case UndefinedSchemaIdHandling.UseAssemblyQualifiedName: + return type.AssemblyQualifiedName; + default: + return null; + } + } + + private JsonSchema GenerateInternal(Type type, Required valueRequired, bool required) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + string resolvedId = GetTypeId(type, false); + string explicitId = GetTypeId(type, true); + + if (!string.IsNullOrEmpty(resolvedId)) + { + JsonSchema resolvedSchema = _resolver.GetSchema(resolvedId); + if (resolvedSchema != null) + { + // resolved schema is not null but referencing member allows nulls + // change resolved schema to allow nulls. hacky but what are ya gonna do? + if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null)) + resolvedSchema.Type |= JsonSchemaType.Null; + if (required && resolvedSchema.Required != true) + resolvedSchema.Required = true; + + return resolvedSchema; + } + } + + // test for unresolved circular reference + if (_stack.Any(tc => tc.Type == type)) + { + throw new JsonException("Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.".FormatWith(CultureInfo.InvariantCulture, type)); + } + + JsonContract contract = ContractResolver.ResolveContract(type); + JsonConverter converter; + if ((converter = contract.Converter) != null || (converter = contract.InternalConverter) != null) + { + JsonSchema converterSchema = converter.GetSchema(); + if (converterSchema != null) + return converterSchema; + } + + Push(new TypeSchema(type, new JsonSchema())); + + if (explicitId != null) + CurrentSchema.Id = explicitId; + + if (required) + CurrentSchema.Required = true; + CurrentSchema.Title = GetTitle(type); + CurrentSchema.Description = GetDescription(type); + + if (converter != null) + { + // todo: Add GetSchema to JsonConverter and use here? + CurrentSchema.Type = JsonSchemaType.Any; + } + else + { + switch (contract.ContractType) + { + case JsonContractType.Object: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateObjectSchema(type, (JsonObjectContract)contract); + break; + case JsonContractType.Array: + CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired); + + CurrentSchema.Id = GetTypeId(type, false); + + JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetCachedAttribute(type); + bool allowNullItem = (arrayAttribute == null || arrayAttribute.AllowNullItems); + + Type collectionItemType = ReflectionUtils.GetCollectionItemType(type); + if (collectionItemType != null) + { + CurrentSchema.Items = new List(); + CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false)); + } + break; + case JsonContractType.Primitive: + CurrentSchema.Type = GetJsonSchemaType(type, valueRequired); + + if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum() && !type.IsDefined(typeof(FlagsAttribute), true)) + { + CurrentSchema.Enum = new List(); + + IList> enumValues = EnumUtils.GetNamesAndValues(type); + foreach (EnumValue enumValue in enumValues) + { + JToken value = JToken.FromObject(enumValue.Value); + + CurrentSchema.Enum.Add(value); + } + } + break; + case JsonContractType.String: + JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType)) + ? JsonSchemaType.String + : AddNullType(JsonSchemaType.String, valueRequired); + + CurrentSchema.Type = schemaType; + break; + case JsonContractType.Dictionary: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + + Type keyType; + Type valueType; + ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType); + + if (keyType != null) + { + JsonContract keyContract = ContractResolver.ResolveContract(keyType); + + // can be converted to a string + if (keyContract.ContractType == JsonContractType.Primitive) + { + CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false); + } + } + break; +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + case JsonContractType.Serializable: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateISerializableContract(type, (JsonISerializableContract)contract); + break; +#endif +#if !(NET35 || NET20 || PORTABLE40) + case JsonContractType.Dynamic: +#endif + case JsonContractType.Linq: + CurrentSchema.Type = JsonSchemaType.Any; + break; + default: + throw new JsonException("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract)); + } + } + + return Pop().Schema; + } + + private JsonSchemaType AddNullType(JsonSchemaType type, Required valueRequired) + { + if (valueRequired != Required.Always) + return type | JsonSchemaType.Null; + + return type; + } + + private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag) + { + return ((value & flag) == flag); + } + + private void GenerateObjectSchema(Type type, JsonObjectContract contract) + { + CurrentSchema.Properties = new Dictionary(); + foreach (JsonProperty property in contract.Properties) + { + if (!property.Ignored) + { + bool optional = property.NullValueHandling == NullValueHandling.Ignore || + HasFlag(property.DefaultValueHandling.GetValueOrDefault(), DefaultValueHandling.Ignore) || + property.ShouldSerialize != null || + property.GetIsSpecified != null; + + JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required, !optional); + + if (property.DefaultValue != null) + propertySchema.Default = JToken.FromObject(property.DefaultValue); + + CurrentSchema.Properties.Add(property.PropertyName, propertySchema); + } + } + + if (type.IsSealed()) + CurrentSchema.AllowAdditionalProperties = false; + } + +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + private void GenerateISerializableContract(Type type, JsonISerializableContract contract) + { + CurrentSchema.AllowAdditionalProperties = true; + } +#endif + + internal static bool HasFlag(JsonSchemaType? value, JsonSchemaType flag) + { + // default value is Any + if (value == null) + return true; + + bool match = ((value & flag) == flag); + if (match) + return true; + + // integer is a subset of float + if (flag == JsonSchemaType.Integer && (value & JsonSchemaType.Float) == JsonSchemaType.Float) + return true; + + return false; + } + + private JsonSchemaType GetJsonSchemaType(Type type, Required valueRequired) + { + JsonSchemaType schemaType = JsonSchemaType.None; + if (valueRequired != Required.Always && ReflectionUtils.IsNullable(type)) + { + schemaType = JsonSchemaType.Null; + if (ReflectionUtils.IsNullableType(type)) + type = Nullable.GetUnderlyingType(type); + } + + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(type); + + switch (typeCode) + { + case PrimitiveTypeCode.Empty: + case PrimitiveTypeCode.Object: + return schemaType | JsonSchemaType.String; +#if !(NETFX_CORE || PORTABLE) + case PrimitiveTypeCode.DBNull: + return schemaType | JsonSchemaType.Null; +#endif + case PrimitiveTypeCode.Boolean: + return schemaType | JsonSchemaType.Boolean; + case PrimitiveTypeCode.Char: + return schemaType | JsonSchemaType.String; + case PrimitiveTypeCode.SByte: + case PrimitiveTypeCode.Byte: + case PrimitiveTypeCode.Int16: + case PrimitiveTypeCode.UInt16: + case PrimitiveTypeCode.Int32: + case PrimitiveTypeCode.UInt32: + case PrimitiveTypeCode.Int64: + case PrimitiveTypeCode.UInt64: +#if !(PORTABLE || NET35 || NET20) + case PrimitiveTypeCode.BigInteger: +#endif + return schemaType | JsonSchemaType.Integer; + case PrimitiveTypeCode.Single: + case PrimitiveTypeCode.Double: + case PrimitiveTypeCode.Decimal: + return schemaType | JsonSchemaType.Float; + // convert to string? + case PrimitiveTypeCode.DateTime: +#if !NET20 + case PrimitiveTypeCode.DateTimeOffset: +#endif + return schemaType | JsonSchemaType.String; + case PrimitiveTypeCode.String: + case PrimitiveTypeCode.Uri: + case PrimitiveTypeCode.Guid: + case PrimitiveTypeCode.TimeSpan: + case PrimitiveTypeCode.Bytes: + return schemaType | JsonSchemaType.String; + default: + throw new JsonException("Unexpected type code '{0}' for type '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeCode, type)); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaModel.cs b/Libs/JsonNet/Schema/JsonSchemaModel.cs new file mode 100644 index 0000000..5a369fc --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaModel.cs @@ -0,0 +1,119 @@ +#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.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaModel + { + public bool Required { get; set; } + public JsonSchemaType Type { get; set; } + public int? MinimumLength { get; set; } + public int? MaximumLength { get; set; } + public double? DivisibleBy { get; set; } + public double? Minimum { get; set; } + public double? Maximum { get; set; } + public bool ExclusiveMinimum { get; set; } + public bool ExclusiveMaximum { get; set; } + public int? MinimumItems { get; set; } + public int? MaximumItems { get; set; } + public IList Patterns { get; set; } + public IList Items { get; set; } + public IDictionary Properties { get; set; } + public IDictionary PatternProperties { get; set; } + public JsonSchemaModel AdditionalProperties { get; set; } + public JsonSchemaModel AdditionalItems { get; set; } + public bool PositionalItemsValidation { get; set; } + public bool AllowAdditionalProperties { get; set; } + public bool AllowAdditionalItems { get; set; } + public bool UniqueItems { get; set; } + public IList Enum { get; set; } + public JsonSchemaType Disallow { get; set; } + + public JsonSchemaModel() + { + Type = JsonSchemaType.Any; + AllowAdditionalProperties = true; + AllowAdditionalItems = true; + Required = false; + } + + public static JsonSchemaModel Create(IList schemata) + { + JsonSchemaModel model = new JsonSchemaModel(); + + foreach (JsonSchema schema in schemata) + { + Combine(model, schema); + } + + return model; + } + + private static void Combine(JsonSchemaModel model, JsonSchema schema) + { + // Version 3 of the Draft JSON Schema has the default value of Not Required + model.Required = model.Required || (schema.Required ?? false); + model.Type = model.Type & (schema.Type ?? JsonSchemaType.Any); + + model.MinimumLength = MathUtils.Max(model.MinimumLength, schema.MinimumLength); + model.MaximumLength = MathUtils.Min(model.MaximumLength, schema.MaximumLength); + + // not sure what is the best way to combine divisibleBy + model.DivisibleBy = MathUtils.Max(model.DivisibleBy, schema.DivisibleBy); + + model.Minimum = MathUtils.Max(model.Minimum, schema.Minimum); + model.Maximum = MathUtils.Max(model.Maximum, schema.Maximum); + model.ExclusiveMinimum = model.ExclusiveMinimum || (schema.ExclusiveMinimum ?? false); + model.ExclusiveMaximum = model.ExclusiveMaximum || (schema.ExclusiveMaximum ?? false); + + model.MinimumItems = MathUtils.Max(model.MinimumItems, schema.MinimumItems); + model.MaximumItems = MathUtils.Min(model.MaximumItems, schema.MaximumItems); + model.PositionalItemsValidation = model.PositionalItemsValidation || schema.PositionalItemsValidation; + model.AllowAdditionalProperties = model.AllowAdditionalProperties && schema.AllowAdditionalProperties; + model.AllowAdditionalItems = model.AllowAdditionalItems && schema.AllowAdditionalItems; + model.UniqueItems = model.UniqueItems || schema.UniqueItems; + if (schema.Enum != null) + { + if (model.Enum == null) + model.Enum = new List(); + + model.Enum.AddRangeDistinct(schema.Enum, JToken.EqualityComparer); + } + model.Disallow = model.Disallow | (schema.Disallow ?? JsonSchemaType.None); + + if (schema.Pattern != null) + { + if (model.Patterns == null) + model.Patterns = new List(); + + model.Patterns.AddDistinct(schema.Pattern); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaModelBuilder.cs b/Libs/JsonNet/Schema/JsonSchemaModelBuilder.cs new file mode 100644 index 0000000..4f17e3a --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaModelBuilder.cs @@ -0,0 +1,191 @@ +#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.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaModelBuilder + { + private JsonSchemaNodeCollection _nodes = new JsonSchemaNodeCollection(); + private Dictionary _nodeModels = new Dictionary(); + private JsonSchemaNode _node; + + public JsonSchemaModel Build(JsonSchema schema) + { + _nodes = new JsonSchemaNodeCollection(); + _node = AddSchema(null, schema); + + _nodeModels = new Dictionary(); + JsonSchemaModel model = BuildNodeModel(_node); + + return model; + } + + public JsonSchemaNode AddSchema(JsonSchemaNode existingNode, JsonSchema schema) + { + string newId; + if (existingNode != null) + { + if (existingNode.Schemas.Contains(schema)) + return existingNode; + + newId = JsonSchemaNode.GetId(existingNode.Schemas.Union(new[] { schema })); + } + else + { + newId = JsonSchemaNode.GetId(new[] { schema }); + } + + if (_nodes.Contains(newId)) + return _nodes[newId]; + + JsonSchemaNode currentNode = (existingNode != null) + ? existingNode.Combine(schema) + : new JsonSchemaNode(schema); + + _nodes.Add(currentNode); + + AddProperties(schema.Properties, currentNode.Properties); + + AddProperties(schema.PatternProperties, currentNode.PatternProperties); + + if (schema.Items != null) + { + for (int i = 0; i < schema.Items.Count; i++) + { + AddItem(currentNode, i, schema.Items[i]); + } + } + + if (schema.AdditionalItems != null) + AddAdditionalItems(currentNode, schema.AdditionalItems); + + if (schema.AdditionalProperties != null) + AddAdditionalProperties(currentNode, schema.AdditionalProperties); + + if (schema.Extends != null) + { + foreach (JsonSchema jsonSchema in schema.Extends) + { + currentNode = AddSchema(currentNode, jsonSchema); + } + } + + return currentNode; + } + + public void AddProperties(IDictionary source, IDictionary target) + { + if (source != null) + { + foreach (KeyValuePair property in source) + { + AddProperty(target, property.Key, property.Value); + } + } + } + + public void AddProperty(IDictionary target, string propertyName, JsonSchema schema) + { + JsonSchemaNode propertyNode; + target.TryGetValue(propertyName, out propertyNode); + + target[propertyName] = AddSchema(propertyNode, schema); + } + + public void AddItem(JsonSchemaNode parentNode, int index, JsonSchema schema) + { + JsonSchemaNode existingItemNode = (parentNode.Items.Count > index) + ? parentNode.Items[index] + : null; + + JsonSchemaNode newItemNode = AddSchema(existingItemNode, schema); + + if (!(parentNode.Items.Count > index)) + { + parentNode.Items.Add(newItemNode); + } + else + { + parentNode.Items[index] = newItemNode; + } + } + + public void AddAdditionalProperties(JsonSchemaNode parentNode, JsonSchema schema) + { + parentNode.AdditionalProperties = AddSchema(parentNode.AdditionalProperties, schema); + } + + public void AddAdditionalItems(JsonSchemaNode parentNode, JsonSchema schema) + { + parentNode.AdditionalItems = AddSchema(parentNode.AdditionalItems, schema); + } + + private JsonSchemaModel BuildNodeModel(JsonSchemaNode node) + { + JsonSchemaModel model; + if (_nodeModels.TryGetValue(node, out model)) + return model; + + model = JsonSchemaModel.Create(node.Schemas); + _nodeModels[node] = model; + + foreach (KeyValuePair property in node.Properties) + { + if (model.Properties == null) + model.Properties = new Dictionary(); + + model.Properties[property.Key] = BuildNodeModel(property.Value); + } + foreach (KeyValuePair property in node.PatternProperties) + { + if (model.PatternProperties == null) + model.PatternProperties = new Dictionary(); + + model.PatternProperties[property.Key] = BuildNodeModel(property.Value); + } + foreach (JsonSchemaNode t in node.Items) + { + if (model.Items == null) + model.Items = new List(); + + model.Items.Add(BuildNodeModel(t)); + } + if (node.AdditionalProperties != null) + model.AdditionalProperties = BuildNodeModel(node.AdditionalProperties); + if (node.AdditionalItems != null) + model.AdditionalItems = BuildNodeModel(node.AdditionalItems); + + return model; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaNode.cs b/Libs/JsonNet/Schema/JsonSchemaNode.cs new file mode 100644 index 0000000..3cd2c33 --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaNode.cs @@ -0,0 +1,80 @@ +#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.Collections.ObjectModel; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaNode + { + public string Id { get; private set; } + public ReadOnlyCollection Schemas { get; private set; } + public Dictionary Properties { get; private set; } + public Dictionary PatternProperties { get; private set; } + public List Items { get; private set; } + public JsonSchemaNode AdditionalProperties { get; set; } + public JsonSchemaNode AdditionalItems { get; set; } + + public JsonSchemaNode(JsonSchema schema) + { + Schemas = new ReadOnlyCollection(new[] { schema }); + Properties = new Dictionary(); + PatternProperties = new Dictionary(); + Items = new List(); + + Id = GetId(Schemas); + } + + private JsonSchemaNode(JsonSchemaNode source, JsonSchema schema) + { + Schemas = new ReadOnlyCollection(source.Schemas.Union(new[] { schema }).ToList()); + Properties = new Dictionary(source.Properties); + PatternProperties = new Dictionary(source.PatternProperties); + Items = new List(source.Items); + AdditionalProperties = source.AdditionalProperties; + AdditionalItems = source.AdditionalItems; + + Id = GetId(Schemas); + } + + public JsonSchemaNode Combine(JsonSchema schema) + { + return new JsonSchemaNode(this, schema); + } + + public static string GetId(IEnumerable schemata) + { + return string.Join("-", schemata.Select(s => s.InternalId).OrderBy(id => id, StringComparer.Ordinal).ToArray()); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaNodeCollection.cs b/Libs/JsonNet/Schema/JsonSchemaNodeCollection.cs new file mode 100644 index 0000000..008919a --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaNodeCollection.cs @@ -0,0 +1,37 @@ +#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.Collections.ObjectModel; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaNodeCollection : KeyedCollection + { + protected override string GetKeyForItem(JsonSchemaNode item) + { + return item.Id; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaResolver.cs b/Libs/JsonNet/Schema/JsonSchemaResolver.cs new file mode 100644 index 0000000..d38121d --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaResolver.cs @@ -0,0 +1,71 @@ +#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; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Schema +{ + /// + /// Resolves from an id. + /// + public class JsonSchemaResolver + { + /// + /// Gets or sets the loaded schemas. + /// + /// The loaded schemas. + public IList LoadedSchemas { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchemaResolver() + { + LoadedSchemas = new List(); + } + + /// + /// Gets a for the specified reference. + /// + /// The id. + /// A for the specified reference. + public virtual JsonSchema GetSchema(string reference) + { + JsonSchema schema = LoadedSchemas.SingleOrDefault(s => string.Equals(s.Id, reference, StringComparison.Ordinal)); + + if (schema == null) + schema = LoadedSchemas.SingleOrDefault(s => string.Equals(s.Location, reference, StringComparison.Ordinal)); + + return schema; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaType.cs b/Libs/JsonNet/Schema/JsonSchemaType.cs new file mode 100644 index 0000000..5b32805 --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaType.cs @@ -0,0 +1,81 @@ +#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; + +namespace Newtonsoft.Json.Schema +{ + /// + /// The value types allowed by the . + /// + [Flags] + public enum JsonSchemaType + { + /// + /// No type specified. + /// + None = 0, + + /// + /// String type. + /// + String = 1, + + /// + /// Float type. + /// + Float = 2, + + /// + /// Integer type. + /// + Integer = 4, + + /// + /// Boolean type. + /// + Boolean = 8, + + /// + /// Object type. + /// + Object = 16, + + /// + /// Array type. + /// + Array = 32, + + /// + /// Null type. + /// + Null = 64, + + /// + /// Any type. + /// + Any = String | Float | Integer | Boolean | Object | Array | Null + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/JsonSchemaWriter.cs b/Libs/JsonNet/Schema/JsonSchemaWriter.cs new file mode 100644 index 0000000..e5a817b --- /dev/null +++ b/Libs/JsonNet/Schema/JsonSchemaWriter.cs @@ -0,0 +1,243 @@ +#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.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaWriter + { + private readonly JsonWriter _writer; + private readonly JsonSchemaResolver _resolver; + + public JsonSchemaWriter(JsonWriter writer, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + _writer = writer; + _resolver = resolver; + } + + private void ReferenceOrWriteSchema(JsonSchema schema) + { + if (schema.Id != null && _resolver.GetSchema(schema.Id) != null) + { + _writer.WriteStartObject(); + _writer.WritePropertyName(JsonTypeReflector.RefPropertyName); + _writer.WriteValue(schema.Id); + _writer.WriteEndObject(); + } + else + { + WriteSchema(schema); + } + } + + public void WriteSchema(JsonSchema schema) + { + ValidationUtils.ArgumentNotNull(schema, "schema"); + + if (!_resolver.LoadedSchemas.Contains(schema)) + _resolver.LoadedSchemas.Add(schema); + + _writer.WriteStartObject(); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.IdPropertyName, schema.Id); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.TitlePropertyName, schema.Title); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.DescriptionPropertyName, schema.Description); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.RequiredPropertyName, schema.Required); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ReadOnlyPropertyName, schema.ReadOnly); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.HiddenPropertyName, schema.Hidden); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.TransientPropertyName, schema.Transient); + if (schema.Type != null) + WriteType(JsonSchemaConstants.TypePropertyName, _writer, schema.Type.Value); + if (!schema.AllowAdditionalProperties) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalPropertiesPropertyName); + _writer.WriteValue(schema.AllowAdditionalProperties); + } + else + { + if (schema.AdditionalProperties != null) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalPropertiesPropertyName); + ReferenceOrWriteSchema(schema.AdditionalProperties); + } + } + if (!schema.AllowAdditionalItems) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalItemsPropertyName); + _writer.WriteValue(schema.AllowAdditionalItems); + } + else + { + if (schema.AdditionalItems != null) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalItemsPropertyName); + ReferenceOrWriteSchema(schema.AdditionalItems); + } + } + WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PropertiesPropertyName, schema.Properties); + WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PatternPropertiesPropertyName, schema.PatternProperties); + WriteItems(schema); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumPropertyName, schema.Minimum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumPropertyName, schema.Maximum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMinimumPropertyName, schema.ExclusiveMinimum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMaximumPropertyName, schema.ExclusiveMaximum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumLengthPropertyName, schema.MinimumLength); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumLengthPropertyName, schema.MaximumLength); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumItemsPropertyName, schema.MinimumItems); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumItemsPropertyName, schema.MaximumItems); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.DivisibleByPropertyName, schema.DivisibleBy); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.FormatPropertyName, schema.Format); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.PatternPropertyName, schema.Pattern); + if (schema.Enum != null) + { + _writer.WritePropertyName(JsonSchemaConstants.EnumPropertyName); + _writer.WriteStartArray(); + foreach (JToken token in schema.Enum) + { + token.WriteTo(_writer); + } + _writer.WriteEndArray(); + } + if (schema.Default != null) + { + _writer.WritePropertyName(JsonSchemaConstants.DefaultPropertyName); + schema.Default.WriteTo(_writer); + } + if (schema.Disallow != null) + WriteType(JsonSchemaConstants.DisallowPropertyName, _writer, schema.Disallow.Value); + if (schema.Extends != null && schema.Extends.Count > 0) + { + _writer.WritePropertyName(JsonSchemaConstants.ExtendsPropertyName); + if (schema.Extends.Count == 1) + { + ReferenceOrWriteSchema(schema.Extends[0]); + } + else + { + _writer.WriteStartArray(); + foreach (JsonSchema jsonSchema in schema.Extends) + { + ReferenceOrWriteSchema(jsonSchema); + } + _writer.WriteEndArray(); + } + } + _writer.WriteEndObject(); + } + + private void WriteSchemaDictionaryIfNotNull(JsonWriter writer, string propertyName, IDictionary properties) + { + if (properties != null) + { + writer.WritePropertyName(propertyName); + writer.WriteStartObject(); + foreach (KeyValuePair property in properties) + { + writer.WritePropertyName(property.Key); + ReferenceOrWriteSchema(property.Value); + } + writer.WriteEndObject(); + } + } + + private void WriteItems(JsonSchema schema) + { + if (schema.Items == null && !schema.PositionalItemsValidation) + return; + + _writer.WritePropertyName(JsonSchemaConstants.ItemsPropertyName); + + if (!schema.PositionalItemsValidation) + { + if (schema.Items != null && schema.Items.Count > 0) + { + ReferenceOrWriteSchema(schema.Items[0]); + } + else + { + _writer.WriteStartObject(); + _writer.WriteEndObject(); + } + return; + } + + _writer.WriteStartArray(); + if (schema.Items != null) + { + foreach (JsonSchema itemSchema in schema.Items) + { + ReferenceOrWriteSchema(itemSchema); + } + } + _writer.WriteEndArray(); + } + + private void WriteType(string propertyName, JsonWriter writer, JsonSchemaType type) + { + IList types; + if (System.Enum.IsDefined(typeof(JsonSchemaType), type)) + types = new List { type }; + else + types = EnumUtils.GetFlagsValues(type).Where(v => v != JsonSchemaType.None).ToList(); + + if (types.Count == 0) + return; + + writer.WritePropertyName(propertyName); + + if (types.Count == 1) + { + writer.WriteValue(JsonSchemaBuilder.MapType(types[0])); + return; + } + + writer.WriteStartArray(); + foreach (JsonSchemaType jsonSchemaType in types) + { + writer.WriteValue(JsonSchemaBuilder.MapType(jsonSchemaType)); + } + writer.WriteEndArray(); + } + + private void WritePropertyIfNotNull(JsonWriter writer, string propertyName, object value) + { + if (value != null) + { + writer.WritePropertyName(propertyName); + writer.WriteValue(value); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/UndefinedSchemaIdHandling.cs b/Libs/JsonNet/Schema/UndefinedSchemaIdHandling.cs new file mode 100644 index 0000000..43c7653 --- /dev/null +++ b/Libs/JsonNet/Schema/UndefinedSchemaIdHandling.cs @@ -0,0 +1,48 @@ +#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 + +namespace Newtonsoft.Json.Schema +{ + /// + /// Specifies undefined schema Id handling options for the . + /// + public enum UndefinedSchemaIdHandling + { + /// + /// Do not infer a schema Id. + /// + None = 0, + + /// + /// Use the .NET type name as the schema Id. + /// + UseTypeName = 1, + + /// + /// Use the assembly qualified .NET type name as the schema Id. + /// + UseAssemblyQualifiedName = 2, + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/ValidationEventArgs.cs b/Libs/JsonNet/Schema/ValidationEventArgs.cs new file mode 100644 index 0000000..86173a9 --- /dev/null +++ b/Libs/JsonNet/Schema/ValidationEventArgs.cs @@ -0,0 +1,71 @@ +#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 Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Returns detailed information related to the . + /// + public class ValidationEventArgs : EventArgs + { + private readonly JsonSchemaException _ex; + + internal ValidationEventArgs(JsonSchemaException ex) + { + ValidationUtils.ArgumentNotNull(ex, "ex"); + _ex = ex; + } + + /// + /// Gets the associated with the validation error. + /// + /// The JsonSchemaException associated with the validation error. + public JsonSchemaException Exception + { + get { return _ex; } + } + + /// + /// Gets the path of the JSON location where the validation error occurred. + /// + /// The path of the JSON location where the validation error occurred. + public string Path + { + get { return _ex.Path; } + } + + /// + /// Gets the text description corresponding to the validation error. + /// + /// The text description. + public string Message + { + get { return _ex.Message; } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Schema/ValidationEventHandler.cs b/Libs/JsonNet/Schema/ValidationEventHandler.cs new file mode 100644 index 0000000..80294f1 --- /dev/null +++ b/Libs/JsonNet/Schema/ValidationEventHandler.cs @@ -0,0 +1,32 @@ +#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 + +namespace Newtonsoft.Json.Schema +{ + /// + /// Represents the callback method that will handle JSON schema validation events and the . + /// + public delegate void ValidationEventHandler(object sender, ValidationEventArgs e); +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/CachedAttributeGetter.cs b/Libs/JsonNet/Serialization/CachedAttributeGetter.cs new file mode 100644 index 0000000..0645631 --- /dev/null +++ b/Libs/JsonNet/Serialization/CachedAttributeGetter.cs @@ -0,0 +1,41 @@ +#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.Reflection; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal static class CachedAttributeGetter where T : Attribute + { + private static readonly ThreadSafeStore TypeAttributeCache = new ThreadSafeStore(JsonTypeReflector.GetAttribute); + + public static T GetAttribute(object type) + { + return TypeAttributeCache.Get(type); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/CamelCasePropertyNamesContractResolver.cs b/Libs/JsonNet/Serialization/CamelCasePropertyNamesContractResolver.cs new file mode 100644 index 0000000..e821e48 --- /dev/null +++ b/Libs/JsonNet/Serialization/CamelCasePropertyNamesContractResolver.cs @@ -0,0 +1,55 @@ +#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.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Resolves member mappings for a type, camel casing property names. + /// + public class CamelCasePropertyNamesContractResolver : DefaultContractResolver + { + /// + /// Initializes a new instance of the class. + /// + public CamelCasePropertyNamesContractResolver() + : base(true) + { + } + + /// + /// Resolves the name of the property. + /// + /// Name of the property. + /// The property name camel cased. + protected override string ResolvePropertyName(string propertyName) + { + // lower case the first letter of the passed in name + return StringUtils.ToCamelCase(propertyName); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/DefaultContractResolver.cs b/Libs/JsonNet/Serialization/DefaultContractResolver.cs new file mode 100644 index 0000000..fc7508e --- /dev/null +++ b/Libs/JsonNet/Serialization/DefaultContractResolver.cs @@ -0,0 +1,1363 @@ +#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; +#if !(NET35 || NET20 || PORTABLE || PORTABLE40) +using System.Collections.Concurrent; +#endif +using System.Collections.Generic; +using System.ComponentModel; +#if !(NET35 || NET20 || PORTABLE40) +using System.Dynamic; +#endif +using System.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +#if !(NETFX_CORE || PORTABLE || PORTABLE40) +using System.Security.Permissions; +#endif +using System.Xml.Serialization; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +using System.Runtime.CompilerServices; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Serialization +{ + internal struct ResolverContractKey : IEquatable + { + private readonly Type _resolverType; + private readonly Type _contractType; + + public ResolverContractKey(Type resolverType, Type contractType) + { + _resolverType = resolverType; + _contractType = contractType; + } + + public override int GetHashCode() + { + return _resolverType.GetHashCode() ^ _contractType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is ResolverContractKey)) + return false; + + return Equals((ResolverContractKey)obj); + } + + public bool Equals(ResolverContractKey other) + { + return (_resolverType == other._resolverType && _contractType == other._contractType); + } + } + + internal class DefaultContractResolverState + { + public Dictionary ContractCache; + public PropertyNameTable NameTable = new PropertyNameTable(); + } + + /// + /// Used by to resolves a for a given . + /// + public class DefaultContractResolver : IContractResolver + { + private static readonly IContractResolver _instance = new DefaultContractResolver(true); + + internal static IContractResolver Instance + { + get { return _instance; } + } + + private static readonly JsonConverter[] BuiltInConverters = + { +#if !(NET20 || NETFX_CORE || PORTABLE40 || PORTABLE) + new EntityKeyMemberConverter(), +#endif +#if !(NET35 || NET20 || PORTABLE40) + new ExpandoObjectConverter(), +#endif +#if !(PORTABLE40) + new XmlNodeConverter(), +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + new BinaryConverter(), + new DataSetConverter(), + new DataTableConverter(), +#endif +#if NETFX_CORE + new JsonValueConverter(), +#endif +#if !(NET35 || NET20 || NETFX_CORE) + new DiscriminatedUnionConverter(), +#endif + new KeyValuePairConverter(), + new BsonObjectIdConverter(), + new RegexConverter() + }; + + private static readonly object TypeContractCacheLock = new object(); + + private static readonly DefaultContractResolverState _sharedState = new DefaultContractResolverState(); + private readonly DefaultContractResolverState _instanceState = new DefaultContractResolverState(); + private readonly bool _sharedCache; + + /// + /// Gets a value indicating whether members are being get and set using dynamic code generation. + /// This value is determined by the runtime permissions available. + /// + /// + /// true if using dynamic code generation; otherwise, false. + /// + public bool DynamicCodeGeneration + { + get { return JsonTypeReflector.DynamicCodeGeneration; } + } + +#if !(NETFX_CORE || PORTABLE) + /// + /// Gets or sets the default members search flags. + /// + /// The default members search flags. + [ObsoleteAttribute("DefaultMembersSearchFlags is obsolete. To modify the members serialized inherit from DefaultContractResolver and override the GetSerializableMembers method instead.")] + public BindingFlags DefaultMembersSearchFlags { get; set; } +#else + private BindingFlags DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public; +#endif + + /// + /// Gets or sets a value indicating whether compiler generated members should be serialized. + /// + /// + /// true if serialized compiler generated members; otherwise, false. + /// + public bool SerializeCompilerGeneratedMembers { get; set; } + +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + /// + /// Gets or sets a value indicating whether to ignore the interface when serializing and deserializing types. + /// + /// + /// true if the interface will be ignored when serializing and deserializing types; otherwise, false. + /// + public bool IgnoreSerializableInterface { get; set; } + + /// + /// Gets or sets a value indicating whether to ignore the attribute when serializing and deserializing types. + /// + /// + /// true if the attribute will be ignored when serializing and deserializing types; otherwise, false. + /// + public bool IgnoreSerializableAttribute { get; set; } +#endif + + /// + /// Initializes a new instance of the class. + /// + public DefaultContractResolver() + : this(false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// If set to true the will use a cached shared with other resolvers of the same type. + /// Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only + /// happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different + /// results. When set to false it is highly recommended to reuse instances with the . + /// + public DefaultContractResolver(bool shareCache) + { +#if !NETFX_CORE +#pragma warning disable 618 + DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance; +#pragma warning restore 618 +#endif +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + IgnoreSerializableAttribute = true; +#endif + + _sharedCache = shareCache; + } + + internal DefaultContractResolverState GetState() + { + if (_sharedCache) + return _sharedState; + else + return _instanceState; + } + + /// + /// Resolves the contract for a given type. + /// + /// The type to resolve a contract for. + /// The contract for a given type. + public virtual JsonContract ResolveContract(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + DefaultContractResolverState state = GetState(); + + JsonContract contract; + ResolverContractKey key = new ResolverContractKey(GetType(), type); + Dictionary cache = state.ContractCache; + if (cache == null || !cache.TryGetValue(key, out contract)) + { + contract = CreateContract(type); + + // avoid the possibility of modifying the cache dictionary while another thread is accessing it + lock (TypeContractCacheLock) + { + cache = state.ContractCache; + Dictionary updatedCache = (cache != null) + ? new Dictionary(cache) + : new Dictionary(); + updatedCache[key] = contract; + + state.ContractCache = updatedCache; + } + } + + return contract; + } + + /// + /// Gets the serializable members for the type. + /// + /// The type to get serializable members for. + /// The serializable members for the type. + protected virtual List GetSerializableMembers(Type objectType) + { + bool ignoreSerializableAttribute; +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + ignoreSerializableAttribute = IgnoreSerializableAttribute; +#else + ignoreSerializableAttribute = true; +#endif + + MemberSerialization memberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType, ignoreSerializableAttribute); + + List allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) + .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); + + List serializableMembers = new List(); + + if (memberSerialization != MemberSerialization.Fields) + { +#if !NET20 + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); +#endif + +#pragma warning disable 618 + List defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags) + .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); +#pragma warning restore 618 + + foreach (MemberInfo member in allMembers) + { + // exclude members that are compiler generated if set + if (SerializeCompilerGeneratedMembers || !member.IsDefined(typeof(CompilerGeneratedAttribute), true)) + { + if (defaultMembers.Contains(member)) + { + // add all members that are found by default member search + serializableMembers.Add(member); + } + else + { + // add members that are explicitly marked with JsonProperty/DataMember attribute + // or are a field if serializing just fields + if (JsonTypeReflector.GetAttribute(member) != null) + serializableMembers.Add(member); +#if !NET20 + else if (dataContractAttribute != null && JsonTypeReflector.GetAttribute(member) != null) + serializableMembers.Add(member); +#endif + else if (memberSerialization == MemberSerialization.Fields && member.MemberType() == MemberTypes.Field) + serializableMembers.Add(member); + } + } + } + +#if !NET20 + Type match; + // don't include EntityKey on entities objects... this is a bit hacky + if (objectType.AssignableToTypeName("System.Data.Objects.DataClasses.EntityObject", out match)) + serializableMembers = serializableMembers.Where(ShouldSerializeEntityMember).ToList(); +#endif + } + else + { + // serialize all fields + foreach (MemberInfo member in allMembers) + { + FieldInfo field = member as FieldInfo; + if (field != null && !field.IsStatic) + serializableMembers.Add(member); + } + } + + return serializableMembers; + } + +#if !NET20 + private bool ShouldSerializeEntityMember(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + if (propertyInfo.PropertyType.IsGenericType() && propertyInfo.PropertyType.GetGenericTypeDefinition().FullName == "System.Data.Objects.DataClasses.EntityReference`1") + return false; + } + + return true; + } +#endif + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonObjectContract CreateObjectContract(Type objectType) + { + JsonObjectContract contract = new JsonObjectContract(objectType); + InitializeContract(contract); + + bool ignoreSerializableAttribute; +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + ignoreSerializableAttribute = IgnoreSerializableAttribute; +#else + ignoreSerializableAttribute = true; +#endif + + contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType, ignoreSerializableAttribute); + contract.Properties.AddRange(CreateProperties(contract.NonNullableUnderlyingType, contract.MemberSerialization)); + + JsonObjectAttribute attribute = JsonTypeReflector.GetCachedAttribute(contract.NonNullableUnderlyingType); + if (attribute != null) + contract.ItemRequired = attribute._itemRequired; + + if (contract.IsInstantiable) + { + ConstructorInfo overrideConstructor = GetAttributeConstructor(contract.NonNullableUnderlyingType); + + // check if a JsonConstructorAttribute has been defined and use that + if (overrideConstructor != null) + { +#pragma warning disable 618 + contract.OverrideConstructor = overrideConstructor; +#pragma warning restore 618 + contract.CreatorParameters.AddRange(CreateConstructorParameters(overrideConstructor, contract.Properties)); + } + else if (contract.MemberSerialization == MemberSerialization.Fields) + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // mimic DataContractSerializer behaviour when populating fields by overriding default creator to create an uninitialized object + // note that this is only possible when the application is fully trusted so fall back to using the default constructor (if available) in partial trust + if (JsonTypeReflector.FullyTrusted) + contract.DefaultCreator = contract.GetUninitializedObject; +#endif + } + else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) + { + ConstructorInfo constructor = GetParametrizedConstructor(contract.NonNullableUnderlyingType); + if (constructor != null) + { +#pragma warning disable 618 + contract.ParametrizedConstructor = constructor; +#pragma warning restore 618 + contract.CreatorParameters.AddRange(CreateConstructorParameters(constructor, contract.Properties)); + } + } + } + + MemberInfo extensionDataMember = GetExtensionDataMemberForType(contract.NonNullableUnderlyingType); + if (extensionDataMember != null) + SetExtensionDataDelegates(contract, extensionDataMember); + + return contract; + } + + private MemberInfo GetExtensionDataMemberForType(Type type) + { + IEnumerable members = GetClassHierarchyForType(type).SelectMany(baseType => + { + IList m = new List(); + m.AddRange(baseType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + m.AddRange(baseType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return m; + }); + + MemberInfo extensionDataMember = members.LastOrDefault(m => + { + MemberTypes memberType = m.MemberType(); + if (memberType != MemberTypes.Property && memberType != MemberTypes.Field) + return false; + + // last instance of attribute wins on type if there are multiple + if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false)) + return false; + + Type t = ReflectionUtils.GetMemberUnderlyingType(m); + + Type dictionaryType; + if (ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out dictionaryType)) + { + Type keyType = dictionaryType.GetGenericArguments()[0]; + Type valueType = dictionaryType.GetGenericArguments()[1]; + + if (keyType.IsAssignableFrom(typeof(string)) && valueType.IsAssignableFrom(typeof(JToken))) + return true; + } + + throw new JsonException("Invalid extension data attribute on '{0}'. Member '{1}' type must implement IDictionary.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(m.DeclaringType), m.Name)); + }); + + return extensionDataMember; + } + + private static void SetExtensionDataDelegates(JsonObjectContract contract, MemberInfo member) + { + JsonExtensionDataAttribute extensionDataAttribute = ReflectionUtils.GetAttribute(member); + if (extensionDataAttribute == null) + return; + + Type t = ReflectionUtils.GetMemberUnderlyingType(member); + + Type dictionaryType; + ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out dictionaryType); + + Type keyType = dictionaryType.GetGenericArguments()[0]; + Type valueType = dictionaryType.GetGenericArguments()[1]; + bool isJTokenValueType = typeof(JToken).IsAssignableFrom(valueType); + + Type createdType; + + // change type to a class if it is the base interface so it can be instantiated if needed + if (ReflectionUtils.IsGenericDefinition(t, typeof(IDictionary<,>))) + createdType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + else + createdType = t; + + MethodInfo addMethod = t.GetMethod("Add", new[] { keyType, valueType }); + Func getExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(member); + Action setExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(member); + Func createExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(createdType); + MethodCall setExtensionDataDictionaryValue = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(addMethod); + + ExtensionDataSetter extensionDataSetter = (o, key, value) => + { + object dictionary = getExtensionDataDictionary(o); + if (dictionary == null) + { + dictionary = createExtensionDataDictionary(); + setExtensionDataDictionary(o, dictionary); + } + + // convert object value to JToken so it is compatible with dictionary + // could happen because of primitive types, type name handling and references + if (isJTokenValueType && !(value is JToken)) + value = (value != null) ? JToken.FromObject(value) : JValue.CreateNull(); + + setExtensionDataDictionaryValue(dictionary, key, value); + }; + + Type enumerableWrapper = typeof(DictionaryEnumerator<,>).MakeGenericType(keyType, valueType); + ConstructorInfo constructors = enumerableWrapper.GetConstructors().First(); + ObjectConstructor createEnumerableWrapper = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(constructors); + + ExtensionDataGetter extensionDataGetter = o => + { + object dictionary = getExtensionDataDictionary(o); + if (dictionary == null) + return null; + + return (IEnumerable>)createEnumerableWrapper(dictionary); + }; + + if (extensionDataAttribute.ReadData) + contract.ExtensionDataSetter = extensionDataSetter; + + if (extensionDataAttribute.WriteData) + contract.ExtensionDataGetter = extensionDataGetter; + } + + internal struct DictionaryEnumerator : IEnumerable>, IEnumerator> + { + private readonly IEnumerator> _e; + + public DictionaryEnumerator(IEnumerable> e) + { + ValidationUtils.ArgumentNotNull(e, "e"); + _e = e.GetEnumerator(); + } + + public bool MoveNext() + { + return _e.MoveNext(); + } + + public void Reset() + { + _e.Reset(); + } + + public KeyValuePair Current + { + get { return new KeyValuePair(_e.Current.Key, _e.Current.Value); } + } + + public void Dispose() + { + _e.Dispose(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public IEnumerator> GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + } + + private ConstructorInfo GetAttributeConstructor(Type objectType) + { + IList markedConstructors = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(c => c.IsDefined(typeof(JsonConstructorAttribute), true)).ToList(); + + if (markedConstructors.Count > 1) + throw new JsonException("Multiple constructors with the JsonConstructorAttribute."); + else if (markedConstructors.Count == 1) + return markedConstructors[0]; + + // little hack to get Version objects to deserialize correctly + if (objectType == typeof(Version)) + return objectType.GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int) }); + + return null; + } + + private ConstructorInfo GetParametrizedConstructor(Type objectType) + { + IList constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).ToList(); + + if (constructors.Count == 1) + return constructors[0]; + else + return null; + } + + /// + /// Creates the constructor parameters. + /// + /// The constructor to create properties for. + /// The type's member properties. + /// Properties for the given . + protected virtual IList CreateConstructorParameters(ConstructorInfo constructor, JsonPropertyCollection memberProperties) + { + var constructorParameters = constructor.GetParameters(); + + JsonPropertyCollection parameterCollection = new JsonPropertyCollection(constructor.DeclaringType); + + foreach (ParameterInfo parameterInfo in constructorParameters) + { + // it is possible to generate a ParameterInfo with a null name using Reflection.Emit + // protect against an ArgumentNullException from GetClosestMatchProperty by testing for null here + JsonProperty matchingMemberProperty = (parameterInfo.Name != null) ? memberProperties.GetClosestMatchProperty(parameterInfo.Name) : null; + + // type must match as well as name + if (matchingMemberProperty != null && matchingMemberProperty.PropertyType != parameterInfo.ParameterType) + matchingMemberProperty = null; + + // ensure that property will have a name from matching property or from parameterinfo + // parameterinfo could have no name if generated by a proxy (I'm looking at you Castle) + if (matchingMemberProperty != null || parameterInfo.Name != null) + { + JsonProperty property = CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); + + if (property != null) + { + parameterCollection.AddProperty(property); + } + } + } + + return parameterCollection; + } + + /// + /// Creates a for the given . + /// + /// The matching member property. + /// The constructor parameter. + /// A created for the given . + protected virtual JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo) + { + JsonProperty property = new JsonProperty(); + property.PropertyType = parameterInfo.ParameterType; + + bool allowNonPublicAccess; + SetPropertySettingsFromAttributes(property, parameterInfo, parameterInfo.Name, parameterInfo.Member.DeclaringType, MemberSerialization.OptOut, out allowNonPublicAccess); + + property.Readable = false; + property.Writable = true; + + // "inherit" values from matching member property if unset on parameter + if (matchingMemberProperty != null) + { + property.PropertyName = (property.PropertyName != parameterInfo.Name) ? property.PropertyName : matchingMemberProperty.PropertyName; + property.Converter = property.Converter ?? matchingMemberProperty.Converter; + property.MemberConverter = property.MemberConverter ?? matchingMemberProperty.MemberConverter; + + if (!property._hasExplicitDefaultValue && matchingMemberProperty._hasExplicitDefaultValue) + property.DefaultValue = matchingMemberProperty.DefaultValue; + + property._required = property._required ?? matchingMemberProperty._required; + property.IsReference = property.IsReference ?? matchingMemberProperty.IsReference; + property.NullValueHandling = property.NullValueHandling ?? matchingMemberProperty.NullValueHandling; + property.DefaultValueHandling = property.DefaultValueHandling ?? matchingMemberProperty.DefaultValueHandling; + property.ReferenceLoopHandling = property.ReferenceLoopHandling ?? matchingMemberProperty.ReferenceLoopHandling; + property.ObjectCreationHandling = property.ObjectCreationHandling ?? matchingMemberProperty.ObjectCreationHandling; + property.TypeNameHandling = property.TypeNameHandling ?? matchingMemberProperty.TypeNameHandling; + } + + return property; + } + + /// + /// Resolves the default for the contract. + /// + /// Type of the object. + /// The contract's default . + protected virtual JsonConverter ResolveContractConverter(Type objectType) + { + return JsonTypeReflector.GetJsonConverter(objectType); + } + + private Func GetDefaultCreator(Type createdType) + { + return JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(createdType); + } + +#if !NET20 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")] +#endif + private void InitializeContract(JsonContract contract) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(contract.NonNullableUnderlyingType); + if (containerAttribute != null) + { + contract.IsReference = containerAttribute._isReference; + } +#if !NET20 + else + { + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(contract.NonNullableUnderlyingType); + // doesn't have a null value + if (dataContractAttribute != null && dataContractAttribute.IsReference) + contract.IsReference = true; + } +#endif + + contract.Converter = ResolveContractConverter(contract.NonNullableUnderlyingType); + + // then see whether object is compadible with any of the built in converters + contract.InternalConverter = JsonSerializer.GetMatchingConverter(BuiltInConverters, contract.NonNullableUnderlyingType); + + if (contract.IsInstantiable + && (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true) || contract.CreatedType.IsValueType())) + { + contract.DefaultCreator = GetDefaultCreator(contract.CreatedType); + + contract.DefaultCreatorNonPublic = (!contract.CreatedType.IsValueType() && + ReflectionUtils.GetDefaultConstructor(contract.CreatedType) == null); + } + + ResolveCallbackMethods(contract, contract.NonNullableUnderlyingType); + } + + private void ResolveCallbackMethods(JsonContract contract, Type t) + { + List onSerializing; + List onSerialized; + List onDeserializing; + List onDeserialized; + List onError; + + GetCallbackMethodsForType(t, out onSerializing, out onSerialized, out onDeserializing, out onDeserialized, out onError); + + if (onSerializing != null) + { +#if !(NET35 || NET20 || NETFX_CORE) + if (t.Name != FSharpUtils.FSharpSetTypeName && t.Name != FSharpUtils.FSharpMapTypeName) +#endif + { +#if NETFX_CORE + if (!t.IsGenericType() || (t.GetGenericTypeDefinition() != typeof(ConcurrentDictionary<,>))) + contract.OnSerializingCallbacks.AddRange(onSerializing); +#else + contract.OnSerializingCallbacks.AddRange(onSerializing); +#endif + } + } + + if (onSerialized != null) + contract.OnSerializedCallbacks.AddRange(onSerialized); + + if (onDeserializing != null) + { + contract.OnDeserializingCallbacks.AddRange(onDeserializing); + } + + if (onDeserialized != null) + { +#if !(NET35 || NET20 || NETFX_CORE) + if (t.Name != FSharpUtils.FSharpSetTypeName && t.Name != FSharpUtils.FSharpMapTypeName) +#endif + { + // ConcurrentDictionary throws an error here so don't use its OnDeserialized - http://json.codeplex.com/discussions/257093 +#if !(NET35 || NET20 || PORTABLE || PORTABLE40) + if (!t.IsGenericType() || (t.GetGenericTypeDefinition() != typeof(ConcurrentDictionary<,>))) + contract.OnDeserializedCallbacks.AddRange(onDeserialized); +#else + contract.OnDeserializedCallbacks.AddRange(onDeserialized); +#endif + } + } + + if (onError != null) + contract.OnErrorCallbacks.AddRange(onError); + } + + private void GetCallbackMethodsForType(Type type, out List onSerializing, out List onSerialized, out List onDeserializing, out List onDeserialized, out List onError) + { + onSerializing = null; + onSerialized = null; + onDeserializing = null; + onDeserialized = null; + onError = null; + + foreach (Type baseType in GetClassHierarchyForType(type)) + { + // while we allow more than one OnSerialized total, only one can be defined per class + MethodInfo currentOnSerializing = null; + MethodInfo currentOnSerialized = null; + MethodInfo currentOnDeserializing = null; + MethodInfo currentOnDeserialized = null; + MethodInfo currentOnError = null; + + foreach (MethodInfo method in baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + // compact framework errors when getting parameters for a generic method + // lame, but generic methods should not be callbacks anyway + if (method.ContainsGenericParameters) + continue; + + Type prevAttributeType = null; + ParameterInfo[] parameters = method.GetParameters(); + + if (IsValidCallback(method, parameters, typeof(OnSerializingAttribute), currentOnSerializing, ref prevAttributeType)) + { + onSerializing = onSerializing ?? new List(); + onSerializing.Add(JsonContract.CreateSerializationCallback(method)); + currentOnSerializing = method; + } + if (IsValidCallback(method, parameters, typeof(OnSerializedAttribute), currentOnSerialized, ref prevAttributeType)) + { + onSerialized = onSerialized ?? new List(); + onSerialized.Add(JsonContract.CreateSerializationCallback(method)); + currentOnSerialized = method; + } + if (IsValidCallback(method, parameters, typeof(OnDeserializingAttribute), currentOnDeserializing, ref prevAttributeType)) + { + onDeserializing = onDeserializing ?? new List(); + onDeserializing.Add(JsonContract.CreateSerializationCallback(method)); + currentOnDeserializing = method; + } + if (IsValidCallback(method, parameters, typeof(OnDeserializedAttribute), currentOnDeserialized, ref prevAttributeType)) + { + onDeserialized = onDeserialized ?? new List(); + onDeserialized.Add(JsonContract.CreateSerializationCallback(method)); + currentOnDeserialized = method; + } + if (IsValidCallback(method, parameters, typeof(OnErrorAttribute), currentOnError, ref prevAttributeType)) + { + onError = onError ?? new List(); + onError.Add(JsonContract.CreateSerializationErrorCallback(method)); + currentOnError = method; + } + } + } + } + + private List GetClassHierarchyForType(Type type) + { + List ret = new List(); + + Type current = type; + while (current != null && current != typeof(object)) + { + ret.Add(current); + current = current.BaseType(); + } + + // Return the class list in order of simple => complex + ret.Reverse(); + return ret; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonDictionaryContract CreateDictionaryContract(Type objectType) + { + JsonDictionaryContract contract = new JsonDictionaryContract(objectType); + InitializeContract(contract); + + contract.PropertyNameResolver = ResolvePropertyName; + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonArrayContract CreateArrayContract(Type objectType) + { + JsonArrayContract contract = new JsonArrayContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonPrimitiveContract CreatePrimitiveContract(Type objectType) + { + JsonPrimitiveContract contract = new JsonPrimitiveContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonLinqContract CreateLinqContract(Type objectType) + { + JsonLinqContract contract = new JsonLinqContract(objectType); + InitializeContract(contract); + + return contract; + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonISerializableContract CreateISerializableContract(Type objectType) + { + JsonISerializableContract contract = new JsonISerializableContract(objectType); + InitializeContract(contract); + + ConstructorInfo constructorInfo = contract.NonNullableUnderlyingType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null); + if (constructorInfo != null) + { + ObjectConstructor creator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(constructorInfo); + + contract.ISerializableCreator = creator; + } + + return contract; + } +#endif + +#if !(NET35 || NET20 || PORTABLE40) + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonDynamicContract CreateDynamicContract(Type objectType) + { + JsonDynamicContract contract = new JsonDynamicContract(objectType); + InitializeContract(contract); + + contract.PropertyNameResolver = ResolvePropertyName; + contract.Properties.AddRange(CreateProperties(objectType, MemberSerialization.OptOut)); + + return contract; + } +#endif + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonStringContract CreateStringContract(Type objectType) + { + JsonStringContract contract = new JsonStringContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Determines which contract type is created for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonContract CreateContract(Type objectType) + { + if (IsJsonPrimitiveType(objectType)) + return CreatePrimitiveContract(objectType); + + Type t = ReflectionUtils.EnsureNotNullableType(objectType); + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(t); + + if (containerAttribute is JsonObjectAttribute) + return CreateObjectContract(objectType); + + if (containerAttribute is JsonArrayAttribute) + return CreateArrayContract(objectType); + + if (containerAttribute is JsonDictionaryAttribute) + return CreateDictionaryContract(objectType); + + if (t == typeof(JToken) || t.IsSubclassOf(typeof(JToken))) + return CreateLinqContract(objectType); + + if (CollectionUtils.IsDictionaryType(t)) + return CreateDictionaryContract(objectType); + + if (typeof(IEnumerable).IsAssignableFrom(t)) + return CreateArrayContract(objectType); + + if (CanConvertToString(t)) + return CreateStringContract(objectType); + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (!IgnoreSerializableInterface && typeof(ISerializable).IsAssignableFrom(t)) + return CreateISerializableContract(objectType); +#endif + +#if !(NET35 || NET20 || PORTABLE40) + if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(t)) + return CreateDynamicContract(objectType); +#endif + +#if !(PORTABLE || NETFX_CORE) + // tested last because it is not possible to automatically deserialize custom IConvertible types + if (IsIConvertible(t)) + return CreatePrimitiveContract(t); +#endif + + return CreateObjectContract(objectType); + } + + internal static bool IsJsonPrimitiveType(Type t) + { + PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(t); + + return (typeCode != PrimitiveTypeCode.Empty && typeCode != PrimitiveTypeCode.Object); + } + +#if !(PORTABLE || NETFX_CORE) + internal static bool IsIConvertible(Type t) + { + if (typeof(IConvertible).IsAssignableFrom(t) + || (ReflectionUtils.IsNullableType(t) && typeof(IConvertible).IsAssignableFrom(Nullable.GetUnderlyingType(t)))) + { + return !typeof(JToken).IsAssignableFrom(t); + } + + return false; + } +#endif + + internal static bool CanConvertToString(Type type) + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + TypeConverter converter = ConvertUtils.GetConverter(type); + + // use the objectType's TypeConverter if it has one and can convert to a string + if (converter != null + && !(converter is ComponentConverter) + && !(converter is ReferenceConverter) + && converter.GetType() != typeof(TypeConverter)) + { + if (converter.CanConvertTo(typeof(string))) + return true; + } +#endif + + if (type == typeof(Type) || type.IsSubclassOf(typeof(Type))) + return true; + + return false; + } + + private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo currentCallback, ref Type prevAttributeType) + { + if (!method.IsDefined(attributeType, false)) + return false; + + if (currentCallback != null) + throw new JsonException("Invalid attribute. Both '{0}' and '{1}' in type '{2}' have '{3}'.".FormatWith(CultureInfo.InvariantCulture, method, currentCallback, GetClrTypeFullName(method.DeclaringType), attributeType)); + + if (prevAttributeType != null) + throw new JsonException("Invalid Callback. Method '{3}' in type '{2}' has both '{0}' and '{1}'.".FormatWith(CultureInfo.InvariantCulture, prevAttributeType, attributeType, GetClrTypeFullName(method.DeclaringType), method)); + + if (method.IsVirtual) + throw new JsonException("Virtual Method '{0}' of type '{1}' cannot be marked with '{2}' attribute.".FormatWith(CultureInfo.InvariantCulture, method, GetClrTypeFullName(method.DeclaringType), attributeType)); + + if (method.ReturnType != typeof(void)) + throw new JsonException("Serialization Callback '{1}' in type '{0}' must return void.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method)); + + if (attributeType == typeof(OnErrorAttribute)) + { + if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext)) + throw new JsonException("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext), typeof(ErrorContext))); + } + else + { + if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext)) + throw new JsonException("Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext))); + } + + prevAttributeType = attributeType; + + return true; + } + + internal static string GetClrTypeFullName(Type type) + { + if (type.IsGenericTypeDefinition() || !type.ContainsGenericParameters()) + return type.FullName; + + return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { type.Namespace, type.Name }); + } + + /// + /// Creates properties for the given . + /// + /// The type to create properties for. + /// /// The member serialization mode for the type. + /// Properties for the given . + protected virtual IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + List members = GetSerializableMembers(type); + if (members == null) + throw new JsonSerializationException("Null collection of seralizable members returned."); + + JsonPropertyCollection properties = new JsonPropertyCollection(type); + + foreach (MemberInfo member in members) + { + JsonProperty property = CreateProperty(member, memberSerialization); + + if (property != null) + { + DefaultContractResolverState state = GetState(); + + // nametable is not thread-safe for multiple writers + lock (state.NameTable) + { + property.PropertyName = state.NameTable.Add(property.PropertyName); + } + + properties.AddProperty(property); + } + } + + IList orderedProperties = properties.OrderBy(p => p.Order ?? -1).ToList(); + return orderedProperties; + } + + /// + /// Creates the used by the serializer to get and set values from a member. + /// + /// The member. + /// The used by the serializer to get and set values from a member. + protected virtual IValueProvider CreateMemberValueProvider(MemberInfo member) + { + // warning - this method use to cause errors with Intellitrace. Retest in VS Ultimate after changes + IValueProvider valueProvider; + +#if !(PORTABLE40 || PORTABLE || NETFX_CORE) + if (DynamicCodeGeneration) + valueProvider = new DynamicValueProvider(member); + else + valueProvider = new ReflectionValueProvider(member); +#elif !(PORTABLE40) + valueProvider = new ExpressionValueProvider(member); +#else + valueProvider = new ReflectionValueProvider(member); +#endif + + return valueProvider; + } + + /// + /// Creates a for the given . + /// + /// The member's parent . + /// The member to create a for. + /// A created for the given . + protected virtual JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = new JsonProperty(); + property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member); + property.DeclaringType = member.DeclaringType; + property.ValueProvider = CreateMemberValueProvider(member); + + bool allowNonPublicAccess; + SetPropertySettingsFromAttributes(property, member, member.Name, member.DeclaringType, memberSerialization, out allowNonPublicAccess); + + if (memberSerialization != MemberSerialization.Fields) + { + property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess); + property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess, property.HasMemberAttribute); + } + else + { + // write to readonly fields + property.Readable = true; + property.Writable = true; + } + property.ShouldSerialize = CreateShouldSerializeTest(member); + + SetIsSpecifiedActions(property, member, allowNonPublicAccess); + + return property; + } + + private void SetPropertySettingsFromAttributes(JsonProperty property, object attributeProvider, string name, Type declaringType, MemberSerialization memberSerialization, out bool allowNonPublicAccess) + { +#if !NET20 + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(declaringType); + + MemberInfo memberInfo = attributeProvider as MemberInfo; + + DataMemberAttribute dataMemberAttribute; + if (dataContractAttribute != null && memberInfo != null) + dataMemberAttribute = JsonTypeReflector.GetDataMemberAttribute((MemberInfo)memberInfo); + else + dataMemberAttribute = null; +#endif + + JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute(attributeProvider); + if (propertyAttribute != null) + property.HasMemberAttribute = true; + + string mappedName; + if (propertyAttribute != null && propertyAttribute.PropertyName != null) + mappedName = propertyAttribute.PropertyName; +#if !NET20 + else if (dataMemberAttribute != null && dataMemberAttribute.Name != null) + mappedName = dataMemberAttribute.Name; +#endif + else + mappedName = name; + + property.PropertyName = ResolvePropertyName(mappedName); + property.UnderlyingName = name; + + bool hasMemberAttribute = false; + if (propertyAttribute != null) + { + property._required = propertyAttribute._required; + property.Order = propertyAttribute._order; + property.DefaultValueHandling = propertyAttribute._defaultValueHandling; + hasMemberAttribute = true; + } +#if !NET20 + else if (dataMemberAttribute != null) + { + property._required = (dataMemberAttribute.IsRequired) ? Required.AllowNull : Required.Default; + property.Order = (dataMemberAttribute.Order != -1) ? (int?)dataMemberAttribute.Order : null; + property.DefaultValueHandling = (!dataMemberAttribute.EmitDefaultValue) ? (DefaultValueHandling?)DefaultValueHandling.Ignore : null; + hasMemberAttribute = true; + } +#endif + + bool hasJsonIgnoreAttribute = + JsonTypeReflector.GetAttribute(attributeProvider) != null + // automatically ignore extension data dictionary property if it is public + || JsonTypeReflector.GetAttribute(attributeProvider) != null +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + || JsonTypeReflector.GetAttribute(attributeProvider) != null +#endif + ; + + if (memberSerialization != MemberSerialization.OptIn) + { + bool hasIgnoreDataMemberAttribute = false; + +#if !(NET20 || NET35) + hasIgnoreDataMemberAttribute = (JsonTypeReflector.GetAttribute(attributeProvider) != null); +#endif + + // ignored if it has JsonIgnore or NonSerialized or IgnoreDataMember attributes + property.Ignored = (hasJsonIgnoreAttribute || hasIgnoreDataMemberAttribute); + } + else + { + // ignored if it has JsonIgnore/NonSerialized or does not have DataMember or JsonProperty attributes + property.Ignored = (hasJsonIgnoreAttribute || !hasMemberAttribute); + } + + // resolve converter for property + // the class type might have a converter but the property converter takes presidence + property.Converter = JsonTypeReflector.GetJsonConverter(attributeProvider); + property.MemberConverter = JsonTypeReflector.GetJsonConverter(attributeProvider); + + DefaultValueAttribute defaultValueAttribute = JsonTypeReflector.GetAttribute(attributeProvider); + if (defaultValueAttribute != null) + property.DefaultValue = defaultValueAttribute.Value; + + property.NullValueHandling = (propertyAttribute != null) ? propertyAttribute._nullValueHandling : null; + property.ReferenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._referenceLoopHandling : null; + property.ObjectCreationHandling = (propertyAttribute != null) ? propertyAttribute._objectCreationHandling : null; + property.TypeNameHandling = (propertyAttribute != null) ? propertyAttribute._typeNameHandling : null; + property.IsReference = (propertyAttribute != null) ? propertyAttribute._isReference : null; + + property.ItemIsReference = (propertyAttribute != null) ? propertyAttribute._itemIsReference : null; + property.ItemConverter = + (propertyAttribute != null && propertyAttribute.ItemConverterType != null) + ? JsonTypeReflector.CreateJsonConverterInstance(propertyAttribute.ItemConverterType, propertyAttribute.ItemConverterParameters) + : null; + property.ItemReferenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._itemReferenceLoopHandling : null; + property.ItemTypeNameHandling = (propertyAttribute != null) ? propertyAttribute._itemTypeNameHandling : null; + + allowNonPublicAccess = false; +#pragma warning disable 618 + if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic) + allowNonPublicAccess = true; +#pragma warning restore 618 + if (propertyAttribute != null) + allowNonPublicAccess = true; + if (memberSerialization == MemberSerialization.Fields) + allowNonPublicAccess = true; + +#if !NET20 + if (dataMemberAttribute != null) + { + allowNonPublicAccess = true; + property.HasMemberAttribute = true; + } +#endif + } + + private Predicate CreateShouldSerializeTest(MemberInfo member) + { + MethodInfo shouldSerializeMethod = member.DeclaringType.GetMethod(JsonTypeReflector.ShouldSerializePrefix + member.Name, ReflectionUtils.EmptyTypes); + + if (shouldSerializeMethod == null || shouldSerializeMethod.ReturnType != typeof(bool)) + return null; + + MethodCall shouldSerializeCall = + JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(shouldSerializeMethod); + + return o => (bool)shouldSerializeCall(o); + } + + private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member, bool allowNonPublicAccess) + { + MemberInfo specifiedMember = member.DeclaringType.GetProperty(member.Name + JsonTypeReflector.SpecifiedPostfix); + if (specifiedMember == null) + specifiedMember = member.DeclaringType.GetField(member.Name + JsonTypeReflector.SpecifiedPostfix); + + if (specifiedMember == null || ReflectionUtils.GetMemberUnderlyingType(specifiedMember) != typeof(bool)) + { + return; + } + + Func specifiedPropertyGet = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(specifiedMember); + + property.GetIsSpecified = o => (bool)specifiedPropertyGet(o); + + if (ReflectionUtils.CanSetMemberValue(specifiedMember, allowNonPublicAccess, false)) + property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(specifiedMember); + } + + /// + /// Resolves the name of the property. + /// + /// Name of the property. + /// Name of the property. + protected virtual string ResolvePropertyName(string propertyName) + { + return propertyName; + } + + /// + /// Gets the resolved name of the property. + /// + /// Name of the property. + /// Name of the property. + public string GetResolvedPropertyName(string propertyName) + { + // this is a new method rather than changing the visibility of ResolvePropertyName to avoid + // a breaking change for anyone who has overidden the method + return ResolvePropertyName(propertyName); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/DefaultReferenceResolver.cs b/Libs/JsonNet/Serialization/DefaultReferenceResolver.cs new file mode 100644 index 0000000..4078799 --- /dev/null +++ b/Libs/JsonNet/Serialization/DefaultReferenceResolver.cs @@ -0,0 +1,83 @@ +#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 Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + internal class DefaultReferenceResolver : IReferenceResolver + { + private int _referenceCount; + + private BidirectionalDictionary GetMappings(object context) + { + JsonSerializerInternalBase internalSerializer; + + if (context is JsonSerializerInternalBase) + internalSerializer = (JsonSerializerInternalBase)context; + else if (context is JsonSerializerProxy) + internalSerializer = ((JsonSerializerProxy)context).GetInternalSerializer(); + else + throw new JsonException("The DefaultReferenceResolver can only be used internally."); + + return internalSerializer.DefaultReferenceMappings; + } + + public object ResolveReference(object context, string reference) + { + object value; + GetMappings(context).TryGetByFirst(reference, out value); + return value; + } + + public string GetReference(object context, object value) + { + var mappings = GetMappings(context); + + string reference; + if (!mappings.TryGetBySecond(value, out reference)) + { + _referenceCount++; + reference = _referenceCount.ToString(CultureInfo.InvariantCulture); + mappings.Set(reference, value); + } + + return reference; + } + + public void AddReference(object context, string reference, object value) + { + GetMappings(context).Set(reference, value); + } + + public bool IsReferenced(object context, object value) + { + string reference; + return GetMappings(context).TryGetBySecond(value, out reference); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/DefaultSerializationBinder.cs b/Libs/JsonNet/Serialization/DefaultSerializationBinder.cs new file mode 100644 index 0000000..98855b4 --- /dev/null +++ b/Libs/JsonNet/Serialization/DefaultSerializationBinder.cs @@ -0,0 +1,158 @@ +#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.Runtime.Serialization; +using System.Reflection; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// The default serialization binder used when resolving and loading classes from type names. + /// + public class DefaultSerializationBinder : SerializationBinder + { + internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder(); + + private readonly ThreadSafeStore _typeCache = new ThreadSafeStore(GetTypeFromTypeNameKey); + + private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey) + { + string assemblyName = typeNameKey.AssemblyName; + string typeName = typeNameKey.TypeName; + + if (assemblyName != null) + { + Assembly assembly; + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // look, I don't like using obsolete methods as much as you do but this is the only way + // Assembly.Load won't check the GAC for a partial name +#pragma warning disable 618,612 + assembly = Assembly.LoadWithPartialName(assemblyName); +#pragma warning restore 618,612 +#elif NETFX_CORE || PORTABLE + assembly = Assembly.Load(new AssemblyName(assemblyName)); +#else + assembly = Assembly.Load(assemblyName); +#endif + +#if !(PORTABLE40 || PORTABLE || NETFX_CORE) + if (assembly == null) + { + // will find assemblies loaded with Assembly.LoadFile outside of the main directory + Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly a in loadedAssemblies) + { + if (a.FullName == assemblyName) + { + assembly = a; + break; + } + } + } +#endif + + if (assembly == null) + throw new JsonSerializationException("Could not load assembly '{0}'.".FormatWith(CultureInfo.InvariantCulture, assemblyName)); + + Type type = assembly.GetType(typeName); + if (type == null) + throw new JsonSerializationException("Could not find type '{0}' in assembly '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeName, assembly.FullName)); + + return type; + } + else + { + return Type.GetType(typeName); + } + } + + internal struct TypeNameKey : IEquatable + { + internal readonly string AssemblyName; + internal readonly string TypeName; + + public TypeNameKey(string assemblyName, string typeName) + { + AssemblyName = assemblyName; + TypeName = typeName; + } + + public override int GetHashCode() + { + return ((AssemblyName != null) ? AssemblyName.GetHashCode() : 0) + ^ ((TypeName != null) ? TypeName.GetHashCode() : 0); + } + + public override bool Equals(object obj) + { + if (!(obj is TypeNameKey)) + return false; + + return Equals((TypeNameKey)obj); + } + + public bool Equals(TypeNameKey other) + { + return (AssemblyName == other.AssemblyName && TypeName == other.TypeName); + } + } + + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + /// + /// The type of the object the formatter creates a new instance of. + /// + public override Type BindToType(string assemblyName, string typeName) + { + return _typeCache.Get(new TypeNameKey(assemblyName, typeName)); + } + +#if !(NET35 || NET20) + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// The type of the object the formatter creates a new instance of. + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + public override void BindToName(Type serializedType, out string assemblyName, out string typeName) + { +#if NETFX_CORE || PORTABLE + assemblyName = serializedType.GetTypeInfo().Assembly.FullName; + typeName = serializedType.FullName; +#else + assemblyName = serializedType.Assembly.FullName; + typeName = serializedType.FullName; +#endif + } +#endif + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/DiagnosticsTraceWriter.cs b/Libs/JsonNet/Serialization/DiagnosticsTraceWriter.cs new file mode 100644 index 0000000..ef00dd2 --- /dev/null +++ b/Libs/JsonNet/Serialization/DiagnosticsTraceWriter.cs @@ -0,0 +1,75 @@ +#if !(PORTABLE40 || PORTABLE || NETFX_CORE) +using System; +using System.Diagnostics; +using DiagnosticsTrace = System.Diagnostics.Trace; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a trace writer that writes to the application's instances. + /// + public class DiagnosticsTraceWriter : ITraceWriter + { + /// + /// Gets the that will be used to filter the trace messages passed to the writer. + /// For example a filter level of Info will exclude Verbose messages and include Info, + /// Warning and Error messages. + /// + /// + /// The that will be used to filter the trace messages passed to the writer. + /// + public TraceLevel LevelFilter { get; set; } + + private TraceEventType GetTraceEventType(TraceLevel level) + { + switch (level) + { + case TraceLevel.Error: + return TraceEventType.Error; + case TraceLevel.Warning: + return TraceEventType.Warning; + case TraceLevel.Info: + return TraceEventType.Information; + case TraceLevel.Verbose: + return TraceEventType.Verbose; + default: + throw new ArgumentOutOfRangeException("level"); + } + } + + /// + /// Writes the specified trace level, message and optional exception. + /// + /// The at which to write this trace. + /// The trace message. + /// The trace exception. This parameter is optional. + public void Trace(TraceLevel level, string message, Exception ex) + { + if (level == TraceLevel.Off) + return; + + TraceEventCache eventCache = new TraceEventCache(); + TraceEventType traceEventType = GetTraceEventType(level); + + foreach (TraceListener listener in DiagnosticsTrace.Listeners) + { + if (!listener.IsThreadSafe) + { + lock (listener) + { + listener.TraceEvent(eventCache, "Newtonsoft.Json", traceEventType, 0, message); + } + } + else + { + listener.TraceEvent(eventCache, "Newtonsoft.Json", traceEventType, 0, message); + } + + if (DiagnosticsTrace.AutoFlush) + listener.Flush(); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/DynamicValueProvider.cs b/Libs/JsonNet/Serialization/DynamicValueProvider.cs new file mode 100644 index 0000000..5c758ae --- /dev/null +++ b/Libs/JsonNet/Serialization/DynamicValueProvider.cs @@ -0,0 +1,113 @@ +#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 !(PORTABLE40 || PORTABLE || NETFX_CORE) +using System; +using System.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +using System.Text; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using dynamic methods. + /// + public class DynamicValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + private Func _getter; + private Action _setter; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public DynamicValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + if (_setter == null) + _setter = DynamicReflectionDelegateFactory.Instance.CreateSet(_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 JsonSerializationException("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); + } + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + { + throw new JsonSerializationException("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); + } +#endif + + _setter(target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + if (_getter == null) + _getter = DynamicReflectionDelegateFactory.Instance.CreateGet(_memberInfo); + + return _getter(target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ErrorContext.cs b/Libs/JsonNet/Serialization/ErrorContext.cs new file mode 100644 index 0000000..bcb2fbb --- /dev/null +++ b/Libs/JsonNet/Serialization/ErrorContext.cs @@ -0,0 +1,75 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides information surrounding an error. + /// + public class ErrorContext + { + internal ErrorContext(object originalObject, object member, string path, Exception error) + { + OriginalObject = originalObject; + Member = member; + Error = error; + Path = path; + } + + internal bool Traced { get; set; } + + /// + /// Gets the error. + /// + /// The error. + public Exception Error { get; private set; } + + /// + /// Gets the original object that caused the error. + /// + /// The original object that caused the error. + public object OriginalObject { get; private set; } + + /// + /// Gets the member that caused the error. + /// + /// The member that caused the error. + public object Member { get; private set; } + + /// + /// Gets the path of the JSON location where the error occurred. + /// + /// The path of the JSON location where the error occurred. + public string Path { get; private set; } + + /// + /// Gets or sets a value indicating whether this is handled. + /// + /// true if handled; otherwise, false. + public bool Handled { get; set; } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ErrorEventArgs.cs b/Libs/JsonNet/Serialization/ErrorEventArgs.cs new file mode 100644 index 0000000..cae7d03 --- /dev/null +++ b/Libs/JsonNet/Serialization/ErrorEventArgs.cs @@ -0,0 +1,58 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides data for the Error event. + /// + public class ErrorEventArgs : EventArgs + { + /// + /// Gets the current object the error event is being raised against. + /// + /// The current object the error event is being raised against. + public object CurrentObject { get; private set; } + + /// + /// Gets the error context. + /// + /// The error context. + public ErrorContext ErrorContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The current object. + /// The error context. + public ErrorEventArgs(object currentObject, ErrorContext errorContext) + { + CurrentObject = currentObject; + ErrorContext = errorContext; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ExpressionValueProvider.cs b/Libs/JsonNet/Serialization/ExpressionValueProvider.cs new file mode 100644 index 0000000..ebb929e --- /dev/null +++ b/Libs/JsonNet/Serialization/ExpressionValueProvider.cs @@ -0,0 +1,114 @@ +#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 !(NET20 || NET35) +using System; +using System.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +using System.Text; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using dynamic methods. + /// + public class ExpressionValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + private Func _getter; + private Action _setter; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public ExpressionValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + if (_setter == null) + _setter = ExpressionReflectionDelegateFactory.Instance.CreateSet(_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 JsonSerializationException("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); + } + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + { + throw new JsonSerializationException("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); + } +#endif + + _setter(target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + if (_getter == null) + _getter = ExpressionReflectionDelegateFactory.Instance.CreateGet(_memberInfo); + + return _getter(target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/IContractResolver.cs b/Libs/JsonNet/Serialization/IContractResolver.cs new file mode 100644 index 0000000..10f44ba --- /dev/null +++ b/Libs/JsonNet/Serialization/IContractResolver.cs @@ -0,0 +1,46 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Used by to resolves a for a given . + /// + /// + /// + /// + /// + public interface IContractResolver + { + /// + /// Resolves the contract for a given type. + /// + /// The type to resolve a contract for. + /// The contract for a given type. + JsonContract ResolveContract(Type type); + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/IReferenceResolver.cs b/Libs/JsonNet/Serialization/IReferenceResolver.cs new file mode 100644 index 0000000..271e307 --- /dev/null +++ b/Libs/JsonNet/Serialization/IReferenceResolver.cs @@ -0,0 +1,67 @@ +#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 + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Used to resolve references when serializing and deserializing JSON by the . + /// + public interface IReferenceResolver + { + /// + /// Resolves a reference to its object. + /// + /// The serialization context. + /// The reference to resolve. + /// The object that + object ResolveReference(object context, string reference); + + /// + /// Gets the reference for the sepecified object. + /// + /// The serialization context. + /// The object to get a reference for. + /// The reference to the object. + string GetReference(object context, object value); + + /// + /// Determines whether the specified object is referenced. + /// + /// The serialization context. + /// The object to test for a reference. + /// + /// true if the specified object is referenced; otherwise, false. + /// + bool IsReferenced(object context, object value); + + /// + /// Adds a reference to the specified object. + /// + /// The serialization context. + /// The reference. + /// The object to reference. + void AddReference(object context, string reference, object value); + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ITraceWriter.cs b/Libs/JsonNet/Serialization/ITraceWriter.cs new file mode 100644 index 0000000..46ed784 --- /dev/null +++ b/Libs/JsonNet/Serialization/ITraceWriter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a trace writer. + /// + public interface ITraceWriter + { + /// + /// Gets the that will be used to filter the trace messages passed to the writer. + /// For example a filter level of Info will exclude Verbose messages and include Info, + /// Warning and Error messages. + /// + /// The that will be used to filter the trace messages passed to the writer. + TraceLevel LevelFilter { get; } + + /// + /// Writes the specified trace level, message and optional exception. + /// + /// The at which to write this trace. + /// The trace message. + /// The trace exception. This parameter is optional. + void Trace(TraceLevel level, string message, Exception ex); + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/IValueProvider.cs b/Libs/JsonNet/Serialization/IValueProvider.cs new file mode 100644 index 0000000..921724f --- /dev/null +++ b/Libs/JsonNet/Serialization/IValueProvider.cs @@ -0,0 +1,47 @@ +#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 + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides methods to get and set values. + /// + public interface IValueProvider + { + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + void SetValue(object target, object value); + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + object GetValue(object target); + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonArrayContract.cs b/Libs/JsonNet/Serialization/JsonArrayContract.cs new file mode 100644 index 0000000..cc9d89a --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonArrayContract.cs @@ -0,0 +1,262 @@ +#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.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonArrayContract : JsonContainerContract + { + /// + /// Gets the of the collection items. + /// + /// The of the collection items. + public Type CollectionItemType { get; private set; } + + /// + /// Gets a value indicating whether the collection type is a multidimensional array. + /// + /// true if the collection type is a multidimensional array; otherwise, false. + public bool IsMultidimensionalArray { get; private set; } + + private readonly Type _genericCollectionDefinitionType; + + private Type _genericWrapperType; + private ObjectConstructor _genericWrapperCreator; + private Func _genericTemporaryCollectionCreator; + + internal bool IsArray { get; private set; } + internal bool ShouldCreateWrapper { get; private set; } + internal bool CanDeserialize { get; private set; } + + private readonly ConstructorInfo _parametrizedConstructor; + + private ObjectConstructor _parametrizedCreator; + internal ObjectConstructor ParametrizedCreator + { + get + { + if (_parametrizedCreator == null) + _parametrizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(_parametrizedConstructor); + + return _parametrizedCreator; + } + } + + internal bool HasParametrizedCreator + { + get { return _parametrizedCreator != null || _parametrizedConstructor != null; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonArrayContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Array; + IsArray = CreatedType.IsArray; + + bool canDeserialize; + + Type tempCollectionType; + if (IsArray) + { + CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType); + IsReadOnlyOrFixedSize = true; + _genericCollectionDefinitionType = typeof(List<>).MakeGenericType(CollectionItemType); + + canDeserialize = true; + IsMultidimensionalArray = (IsArray && UnderlyingType.GetArrayRank() > 1); + } + else if (typeof(IList).IsAssignableFrom(underlyingType)) + { + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) + CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + else + CollectionItemType = ReflectionUtils.GetCollectionItemType(underlyingType); + + if (underlyingType == typeof(IList)) + CreatedType = typeof(List); + + if (CollectionItemType != null) + _parametrizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(underlyingType, CollectionItemType); + + IsReadOnlyOrFixedSize = ReflectionUtils.InheritsGenericDefinition(underlyingType, typeof(ReadOnlyCollection<>)); + canDeserialize = true; + } + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) + { + CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + + if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof(ICollection<>)) + || ReflectionUtils.IsGenericDefinition(underlyingType, typeof(IList<>))) + CreatedType = typeof(List<>).MakeGenericType(CollectionItemType); + +#if !(NET20 || NET35 || PORTABLE40) + if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof(ISet<>))) + CreatedType = typeof(HashSet<>).MakeGenericType(CollectionItemType); +#endif + + _parametrizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(underlyingType, CollectionItemType); + canDeserialize = true; + ShouldCreateWrapper = true; + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IReadOnlyCollection<>), out tempCollectionType)) + { + CollectionItemType = tempCollectionType.GetGenericArguments()[0]; + + if (ReflectionUtils.IsGenericDefinition(underlyingType, typeof(IReadOnlyCollection<>)) + || ReflectionUtils.IsGenericDefinition(underlyingType, typeof(IReadOnlyList<>))) + CreatedType = typeof(ReadOnlyCollection<>).MakeGenericType(CollectionItemType); + + _genericCollectionDefinitionType = typeof(List<>).MakeGenericType(CollectionItemType); + _parametrizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(CreatedType, CollectionItemType); + IsReadOnlyOrFixedSize = true; + canDeserialize = HasParametrizedCreator; + } +#endif + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IEnumerable<>), out tempCollectionType)) + { + CollectionItemType = tempCollectionType.GetGenericArguments()[0]; + + if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IEnumerable<>))) + CreatedType = typeof(List<>).MakeGenericType(CollectionItemType); + + _parametrizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(underlyingType, CollectionItemType); + +#if !(NET35 || NET20 || NETFX_CORE) + if (!HasParametrizedCreator && underlyingType.Name == FSharpUtils.FSharpListTypeName) + { + FSharpUtils.EnsureInitialized(underlyingType.Assembly()); + _parametrizedCreator = FSharpUtils.CreateSeq(CollectionItemType); + } +#endif + + if (underlyingType.IsGenericType() && underlyingType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + _genericCollectionDefinitionType = tempCollectionType; + + IsReadOnlyOrFixedSize = false; + ShouldCreateWrapper = false; + canDeserialize = true; + } + else + { + _genericCollectionDefinitionType = typeof(List<>).MakeGenericType(CollectionItemType); + + IsReadOnlyOrFixedSize = true; + ShouldCreateWrapper = true; + canDeserialize = HasParametrizedCreator; + } + } + else + { + // types that implement IEnumerable and nothing else + canDeserialize = false; + ShouldCreateWrapper = true; + } + + CanDeserialize = canDeserialize; + +#if (NET20 || NET35) + if (CollectionItemType != null && ReflectionUtils.IsNullableType(CollectionItemType)) + { + // bug in .NET 2.0 & 3.5 that List> throws an error when adding null via IList.Add(object) + // wrapper will handle calling Add(T) instead + if (ReflectionUtils.InheritsGenericDefinition(CreatedType, typeof(List<>), out tempCollectionType) + || (IsArray && !IsMultidimensionalArray)) + { + ShouldCreateWrapper = true; + } + } +#endif + +#if !(NET20 || NET35 || NET40 || PORTABLE40) + Type immutableCreatedType; + ObjectConstructor immutableParameterizedCreator; + if (ImmutableCollectionsUtils.TryBuildImmutableForArrayContract(underlyingType, CollectionItemType, out immutableCreatedType, out immutableParameterizedCreator)) + { + CreatedType = immutableCreatedType; + _parametrizedCreator = immutableParameterizedCreator; + IsReadOnlyOrFixedSize = true; + CanDeserialize = true; + } +#endif + } + + internal IWrappedCollection CreateWrapper(object list) + { + if (_genericWrapperCreator == null) + { + _genericWrapperType = typeof(CollectionWrapper<>).MakeGenericType(CollectionItemType); + + Type constructorArgument; + + if (ReflectionUtils.InheritsGenericDefinition(_genericCollectionDefinitionType, typeof(List<>)) + || _genericCollectionDefinitionType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + constructorArgument = typeof(ICollection<>).MakeGenericType(CollectionItemType); + else + constructorArgument = _genericCollectionDefinitionType; + + ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { constructorArgument }); + _genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(genericWrapperConstructor); + } + + return (IWrappedCollection)_genericWrapperCreator(list); + } + + internal IList CreateTemporaryCollection() + { + if (_genericTemporaryCollectionCreator == null) + { + // multidimensional array will also have array instances in it + Type collectionItemType = (IsMultidimensionalArray) ? typeof(object) : CollectionItemType; + Type temporaryListType = typeof(List<>).MakeGenericType(collectionItemType); + _genericTemporaryCollectionCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(temporaryListType); + } + + return (IList)_genericTemporaryCollectionCreator(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonContainerContract.cs b/Libs/JsonNet/Serialization/JsonContainerContract.cs new file mode 100644 index 0000000..287ecb6 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonContainerContract.cs @@ -0,0 +1,117 @@ +#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.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonContainerContract : JsonContract + { + private JsonContract _itemContract; + private JsonContract _finalItemContract; + + // will be null for containers that don't have an item type (e.g. IList) or for complex objects + internal JsonContract ItemContract + { + get { return _itemContract; } + set + { + _itemContract = value; + if (_itemContract != null) + { + _finalItemContract = (_itemContract.UnderlyingType.IsSealed()) ? _itemContract : null; + } + else + { + _finalItemContract = null; + } + } + } + + // the final (i.e. can't be inherited from like a sealed class or valuetype) item contract + internal JsonContract FinalItemContract + { + get { return _finalItemContract; } + } + + /// + /// Gets or sets the default collection items . + /// + /// The converter. + public JsonConverter ItemConverter { get; set; } + + /// + /// Gets or sets a value indicating whether the collection items preserve object references. + /// + /// true if collection items preserve object references; otherwise, false. + public bool? ItemIsReference { get; set; } + + /// + /// Gets or sets the collection item reference loop handling. + /// + /// The reference loop handling. + public ReferenceLoopHandling? ItemReferenceLoopHandling { get; set; } + + /// + /// Gets or sets the collection item type name handling. + /// + /// The type name handling. + public TypeNameHandling? ItemTypeNameHandling { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + internal JsonContainerContract(Type underlyingType) + : base(underlyingType) + { + JsonContainerAttribute jsonContainerAttribute = JsonTypeReflector.GetCachedAttribute(underlyingType); + + if (jsonContainerAttribute != null) + { + if (jsonContainerAttribute.ItemConverterType != null) + ItemConverter = JsonTypeReflector.CreateJsonConverterInstance(jsonContainerAttribute.ItemConverterType, + jsonContainerAttribute.ItemConverterParameters); + + ItemIsReference = jsonContainerAttribute._itemIsReference; + ItemReferenceLoopHandling = jsonContainerAttribute._itemReferenceLoopHandling; + ItemTypeNameHandling = jsonContainerAttribute._itemTypeNameHandling; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonContract.cs b/Libs/JsonNet/Serialization/JsonContract.cs new file mode 100644 index 0000000..dfc8a19 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonContract.cs @@ -0,0 +1,415 @@ +#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; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal enum JsonContractType + { + None, + Object, + Array, + Primitive, + String, + Dictionary, +#if !(NET35 || NET20 || PORTABLE40) + Dynamic, +#endif +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + Serializable, +#endif + Linq + } + + /// + /// Handles serialization callback events. + /// + /// The object that raised the callback event. + /// The streaming context. + public delegate void SerializationCallback(object o, StreamingContext context); + + /// + /// Handles serialization error callback events. + /// + /// The object that raised the callback event. + /// The streaming context. + /// The error context. + public delegate void SerializationErrorCallback(object o, StreamingContext context, ErrorContext errorContext); + + /// + /// Sets extension data for an object during deserialization. + /// + /// The object to set extension data on. + /// The extension data key. + /// The extension data value. + public delegate void ExtensionDataSetter(object o, string key, object value); + + /// + /// Gets extension data for an object during serialization. + /// + /// The object to set extension data on. + public delegate IEnumerable> ExtensionDataGetter(object o); + + /// + /// Contract details for a used by the . + /// + public abstract class JsonContract + { + internal bool IsNullable; + internal bool IsConvertable; + internal bool IsEnum; + internal Type NonNullableUnderlyingType; + internal ReadType InternalReadType; + internal JsonContractType ContractType; + internal bool IsReadOnlyOrFixedSize; + internal bool IsSealed; + internal bool IsInstantiable; + + private List _onDeserializedCallbacks; + private IList _onDeserializingCallbacks; + private IList _onSerializedCallbacks; + private IList _onSerializingCallbacks; + private IList _onErrorCallbacks; + private Type _createdType; + + /// + /// Gets the underlying type for the contract. + /// + /// The underlying type for the contract. + public Type UnderlyingType { get; private set; } + + /// + /// Gets or sets the type created during deserialization. + /// + /// The type created during deserialization. + public Type CreatedType + { + get { return _createdType; } + set + { + _createdType = value; + + IsSealed = _createdType.IsSealed(); + IsInstantiable = !(_createdType.IsInterface() || _createdType.IsAbstract()); + } + } + + /// + /// Gets or sets whether this type contract is serialized as a reference. + /// + /// Whether this type contract is serialized as a reference. + public bool? IsReference { get; set; } + + /// + /// Gets or sets the default for this contract. + /// + /// The converter. + public JsonConverter Converter { get; set; } + + // internally specified JsonConverter's to override default behavour + // checked for after passed in converters and attribute specified converters + internal JsonConverter InternalConverter { get; set; } + + /// + /// Gets or sets all methods called immediately after deserialization of the object. + /// + /// The methods called immediately after deserialization of the object. + public IList OnDeserializedCallbacks + { + get + { + if (_onDeserializedCallbacks == null) + _onDeserializedCallbacks = new List(); + + return _onDeserializedCallbacks; + } + } + + /// + /// Gets or sets all methods called during deserialization of the object. + /// + /// The methods called during deserialization of the object. + public IList OnDeserializingCallbacks + { + get + { + if (_onDeserializingCallbacks == null) + _onDeserializingCallbacks = new List(); + + return _onDeserializingCallbacks; + } + } + + /// + /// Gets or sets all methods called after serialization of the object graph. + /// + /// The methods called after serialization of the object graph. + public IList OnSerializedCallbacks + { + get + { + if (_onSerializedCallbacks == null) + _onSerializedCallbacks = new List(); + + return _onSerializedCallbacks; + } + } + + /// + /// Gets or sets all methods called before serialization of the object. + /// + /// The methods called before serialization of the object. + public IList OnSerializingCallbacks + { + get + { + if (_onSerializingCallbacks == null) + _onSerializingCallbacks = new List(); + + return _onSerializingCallbacks; + } + } + + /// + /// Gets or sets all method called when an error is thrown during the serialization of the object. + /// + /// The methods called when an error is thrown during the serialization of the object. + public IList OnErrorCallbacks + { + get + { + if (_onErrorCallbacks == null) + _onErrorCallbacks = new List(); + + return _onErrorCallbacks; + } + } + + /// + /// Gets or sets the method called immediately after deserialization of the object. + /// + /// The method called immediately after deserialization of the object. + [Obsolete("This property is obsolete and has been replaced by the OnDeserializedCallbacks collection.")] + public MethodInfo OnDeserialized + { + get { return (OnDeserializedCallbacks.Count > 0) ? OnDeserializedCallbacks[0].Method() : null; } + set + { + OnDeserializedCallbacks.Clear(); + OnDeserializedCallbacks.Add(CreateSerializationCallback(value)); + } + } + + /// + /// Gets or sets the method called during deserialization of the object. + /// + /// The method called during deserialization of the object. + [Obsolete("This property is obsolete and has been replaced by the OnDeserializingCallbacks collection.")] + public MethodInfo OnDeserializing + { + get { return (OnDeserializingCallbacks.Count > 0) ? OnDeserializingCallbacks[0].Method() : null; } + set + { + OnDeserializingCallbacks.Clear(); + OnDeserializingCallbacks.Add(CreateSerializationCallback(value)); + } + } + + /// + /// Gets or sets the method called after serialization of the object graph. + /// + /// The method called after serialization of the object graph. + [Obsolete("This property is obsolete and has been replaced by the OnSerializedCallbacks collection.")] + public MethodInfo OnSerialized + { + get { return (OnSerializedCallbacks.Count > 0) ? OnSerializedCallbacks[0].Method() : null; } + set + { + OnSerializedCallbacks.Clear(); + OnSerializedCallbacks.Add(CreateSerializationCallback(value)); + } + } + + /// + /// Gets or sets the method called before serialization of the object. + /// + /// The method called before serialization of the object. + [Obsolete("This property is obsolete and has been replaced by the OnSerializingCallbacks collection.")] + public MethodInfo OnSerializing + { + get { return (OnSerializingCallbacks.Count > 0) ? OnSerializingCallbacks[0].Method() : null; } + set + { + OnSerializingCallbacks.Clear(); + OnSerializingCallbacks.Add(CreateSerializationCallback(value)); + } + } + + /// + /// Gets or sets the method called when an error is thrown during the serialization of the object. + /// + /// The method called when an error is thrown during the serialization of the object. + [Obsolete("This property is obsolete and has been replaced by the OnErrorCallbacks collection.")] + public MethodInfo OnError + { + get { return (OnErrorCallbacks.Count > 0) ? OnErrorCallbacks[0].Method() : null; } + set + { + OnErrorCallbacks.Clear(); + OnErrorCallbacks.Add(CreateSerializationErrorCallback(value)); + } + } + + /// + /// Gets or sets the default creator method used to create the object. + /// + /// The default creator method used to create the object. + public Func DefaultCreator { get; set; } + + /// + /// Gets or sets a value indicating whether the default creator is non public. + /// + /// true if the default object creator is non-public; otherwise, false. + public bool DefaultCreatorNonPublic { get; set; } + + internal JsonContract(Type underlyingType) + { + ValidationUtils.ArgumentNotNull(underlyingType, "underlyingType"); + + UnderlyingType = underlyingType; + + IsNullable = ReflectionUtils.IsNullable(underlyingType); + NonNullableUnderlyingType = (IsNullable && ReflectionUtils.IsNullableType(underlyingType)) ? Nullable.GetUnderlyingType(underlyingType) : underlyingType; + + CreatedType = NonNullableUnderlyingType; + + IsConvertable = ConvertUtils.IsConvertible(NonNullableUnderlyingType); + IsEnum = NonNullableUnderlyingType.IsEnum(); + + if (NonNullableUnderlyingType == typeof(byte[])) + { + InternalReadType = ReadType.ReadAsBytes; + } + else if (NonNullableUnderlyingType == typeof(int)) + { + InternalReadType = ReadType.ReadAsInt32; + } + else if (NonNullableUnderlyingType == typeof(decimal)) + { + InternalReadType = ReadType.ReadAsDecimal; + } + else if (NonNullableUnderlyingType == typeof(string)) + { + InternalReadType = ReadType.ReadAsString; + } + else if (NonNullableUnderlyingType == typeof(DateTime)) + { + InternalReadType = ReadType.ReadAsDateTime; + } +#if !NET20 + else if (NonNullableUnderlyingType == typeof(DateTimeOffset)) + { + InternalReadType = ReadType.ReadAsDateTimeOffset; + } +#endif + else + { + InternalReadType = ReadType.Read; + } + } + + internal void InvokeOnSerializing(object o, StreamingContext context) + { + if (_onSerializingCallbacks != null) + { + foreach (SerializationCallback callback in _onSerializingCallbacks) + { + callback(o, context); + } + } + } + + internal void InvokeOnSerialized(object o, StreamingContext context) + { + if (_onSerializedCallbacks != null) + { + foreach (SerializationCallback callback in _onSerializedCallbacks) + { + callback(o, context); + } + } + } + + internal void InvokeOnDeserializing(object o, StreamingContext context) + { + if (_onDeserializingCallbacks != null) + { + foreach (SerializationCallback callback in _onDeserializingCallbacks) + { + callback(o, context); + } + } + } + + internal void InvokeOnDeserialized(object o, StreamingContext context) + { + if (_onDeserializedCallbacks != null) + { + foreach (SerializationCallback callback in _onDeserializedCallbacks) + { + callback(o, context); + } + } + } + + internal void InvokeOnError(object o, StreamingContext context, ErrorContext errorContext) + { + if (_onErrorCallbacks != null) + { + foreach (SerializationErrorCallback callback in _onErrorCallbacks) + { + callback(o, context, errorContext); + } + } + } + + internal static SerializationCallback CreateSerializationCallback(MethodInfo callbackMethodInfo) + { + return (o, context) => callbackMethodInfo.Invoke(o, new object[] { context }); + } + + internal static SerializationErrorCallback CreateSerializationErrorCallback(MethodInfo callbackMethodInfo) + { + return (o, context, econtext) => callbackMethodInfo.Invoke(o, new object[] { context, econtext }); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonDictionaryContract.cs b/Libs/JsonNet/Serialization/JsonDictionaryContract.cs new file mode 100644 index 0000000..191fec2 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonDictionaryContract.cs @@ -0,0 +1,205 @@ +#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.Collections.ObjectModel; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; + +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonDictionaryContract : JsonContainerContract + { + /// + /// Gets or sets the property name resolver. + /// + /// The property name resolver. + public Func PropertyNameResolver { get; set; } + + /// + /// Gets the of the dictionary keys. + /// + /// The of the dictionary keys. + public Type DictionaryKeyType { get; private set; } + + /// + /// Gets the of the dictionary values. + /// + /// The of the dictionary values. + public Type DictionaryValueType { get; private set; } + + internal JsonContract KeyContract { get; set; } + + private readonly Type _genericCollectionDefinitionType; + + private Type _genericWrapperType; + private ObjectConstructor _genericWrapperCreator; + + private Func _genericTemporaryDictionaryCreator; + + internal bool ShouldCreateWrapper { get; private set; } + + private readonly ConstructorInfo _parametrizedConstructor; + + private ObjectConstructor _parametrizedCreator; + internal ObjectConstructor ParametrizedCreator + { + get + { + if (_parametrizedCreator == null) + _parametrizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(_parametrizedConstructor); + + return _parametrizedCreator; + } + } + + internal bool HasParametrizedCreator + { + get { return _parametrizedCreator != null || _parametrizedConstructor != null; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonDictionaryContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Dictionary; + + Type keyType; + Type valueType; + + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType)) + { + keyType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + valueType = _genericCollectionDefinitionType.GetGenericArguments()[1]; + + if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IDictionary<,>))) + CreatedType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + +#if !(NET40 || NET35 || NET20 || PORTABLE40) + IsReadOnlyOrFixedSize = ReflectionUtils.InheritsGenericDefinition(underlyingType, typeof(ReadOnlyDictionary<,>)); +#endif + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IReadOnlyDictionary<,>), out _genericCollectionDefinitionType)) + { + keyType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + valueType = _genericCollectionDefinitionType.GetGenericArguments()[1]; + + if (ReflectionUtils.IsGenericDefinition(UnderlyingType, typeof(IReadOnlyDictionary<,>))) + CreatedType = typeof(ReadOnlyDictionary<,>).MakeGenericType(keyType, valueType); + + IsReadOnlyOrFixedSize = true; + } +#endif + else + { + ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType); + + if (UnderlyingType == typeof(IDictionary)) + CreatedType = typeof(Dictionary); + } + + if (keyType != null && valueType != null) + { + _parametrizedConstructor = CollectionUtils.ResolveEnumerableCollectionConstructor(CreatedType, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType)); + +#if !(NET35 || NET20 || NETFX_CORE) + if (!HasParametrizedCreator && underlyingType.Name == FSharpUtils.FSharpMapTypeName) + { + FSharpUtils.EnsureInitialized(underlyingType.Assembly()); + _parametrizedCreator = FSharpUtils.CreateMap(keyType, valueType); + } +#endif + } + + ShouldCreateWrapper = !typeof(IDictionary).IsAssignableFrom(CreatedType); + + DictionaryKeyType = keyType; + DictionaryValueType = valueType; + +#if (NET20 || NET35) + if (DictionaryValueType != null && ReflectionUtils.IsNullableType(DictionaryValueType)) + { + Type tempDictioanryType; + + // bug in .NET 2.0 & 3.5 that Dictionary> throws an error when adding null via IDictionary[key] = object + // wrapper will handle calling Add(T) instead + if (ReflectionUtils.InheritsGenericDefinition(CreatedType, typeof(Dictionary<,>), out tempDictioanryType)) + { + ShouldCreateWrapper = true; + } + } +#endif + +#if !(NET20 || NET35 || NET40 || PORTABLE40) + Type immutableCreatedType; + ObjectConstructor immutableParameterizedCreator; + if (ImmutableCollectionsUtils.TryBuildImmutableForDictionaryContract(underlyingType, DictionaryKeyType, DictionaryValueType, out immutableCreatedType, out immutableParameterizedCreator)) + { + CreatedType = immutableCreatedType; + _parametrizedCreator = immutableParameterizedCreator; + IsReadOnlyOrFixedSize = true; + } +#endif + } + + internal IWrappedDictionary CreateWrapper(object dictionary) + { + if (_genericWrapperCreator == null) + { + _genericWrapperType = typeof(DictionaryWrapper<,>).MakeGenericType(DictionaryKeyType, DictionaryValueType); + + ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType }); + _genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(genericWrapperConstructor); + } + + return (IWrappedDictionary)_genericWrapperCreator(dictionary); + } + + internal IDictionary CreateTemporaryDictionary() + { + if (_genericTemporaryDictionaryCreator == null) + { + Type temporaryDictionaryType = typeof(Dictionary<,>).MakeGenericType(DictionaryKeyType, DictionaryValueType); + + _genericTemporaryDictionaryCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(temporaryDictionaryType); + } + + return (IDictionary)_genericTemporaryDictionaryCreator(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonDynamicContract.cs b/Libs/JsonNet/Serialization/JsonDynamicContract.cs new file mode 100644 index 0000000..549ff67 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonDynamicContract.cs @@ -0,0 +1,119 @@ +#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 !(NET35 || NET20 || PORTABLE40) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonDynamicContract : JsonContainerContract + { + /// + /// Gets the object's properties. + /// + /// The object's properties. + public JsonPropertyCollection Properties { get; private set; } + + /// + /// Gets or sets the property name resolver. + /// + /// The property name resolver. + public Func PropertyNameResolver { get; set; } + + private readonly ThreadSafeStore>> _callSiteGetters = + new ThreadSafeStore>>(CreateCallSiteGetter); + + private readonly ThreadSafeStore>> _callSiteSetters = + new ThreadSafeStore>>(CreateCallSiteSetter); + + private static CallSite> CreateCallSiteGetter(string name) + { + GetMemberBinder getMemberBinder = (GetMemberBinder)DynamicUtils.BinderWrapper.GetMember(name, typeof(DynamicUtils)); + + return CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); + } + + private static CallSite> CreateCallSiteSetter(string name) + { + SetMemberBinder binder = (SetMemberBinder)DynamicUtils.BinderWrapper.SetMember(name, typeof(DynamicUtils)); + + return CallSite>.Create(new NoThrowSetBinderMember(binder)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonDynamicContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Dynamic; + + Properties = new JsonPropertyCollection(UnderlyingType); + } + + internal bool TryGetMember(IDynamicMetaObjectProvider dynamicProvider, string name, out object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + CallSite> callSite = _callSiteGetters.Get(name); + + object result = callSite.Target(callSite, dynamicProvider); + + if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult)) + { + value = result; + return true; + } + else + { + value = null; + return false; + } + } + + internal bool TrySetMember(IDynamicMetaObjectProvider dynamicProvider, string name, object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + CallSite> callSite = _callSiteSetters.Get(name); + + object result = callSite.Target(callSite, dynamicProvider, value); + + return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonFormatterConverter.cs b/Libs/JsonNet/Serialization/JsonFormatterConverter.cs new file mode 100644 index 0000000..cc68e9d --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonFormatterConverter.cs @@ -0,0 +1,157 @@ +#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 !(NETFX_CORE || PORTABLE40 || PORTABLE) +using System; +using System.Globalization; +using System.Runtime.Serialization; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonFormatterConverter : IFormatterConverter + { + private readonly JsonSerializerInternalReader _reader; + private readonly JsonISerializableContract _contract; + private readonly JsonProperty _member; + + public JsonFormatterConverter(JsonSerializerInternalReader reader, JsonISerializableContract contract, JsonProperty member) + { + ValidationUtils.ArgumentNotNull(reader, "serializer"); + ValidationUtils.ArgumentNotNull(contract, "contract"); + + _reader = reader; + _contract = contract; + _member = member; + } + + private T GetTokenValue(object value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JValue v = (JValue)value; + return (T)System.Convert.ChangeType(v.Value, typeof(T), CultureInfo.InvariantCulture); + } + + public object Convert(object value, Type type) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JToken token = value as JToken; + if (token == null) + throw new ArgumentException("Value is not a JToken.", "value"); + + return _reader.CreateISerializableItem(token, type, _contract, _member); + } + + public object Convert(object value, TypeCode typeCode) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value is JValue) + value = ((JValue)value).Value; + + return System.Convert.ChangeType(value, typeCode, CultureInfo.InvariantCulture); + } + + public bool ToBoolean(object value) + { + return GetTokenValue(value); + } + + public byte ToByte(object value) + { + return GetTokenValue(value); + } + + public char ToChar(object value) + { + return GetTokenValue(value); + } + + public DateTime ToDateTime(object value) + { + return GetTokenValue(value); + } + + public decimal ToDecimal(object value) + { + return GetTokenValue(value); + } + + public double ToDouble(object value) + { + return GetTokenValue(value); + } + + public short ToInt16(object value) + { + return GetTokenValue(value); + } + + public int ToInt32(object value) + { + return GetTokenValue(value); + } + + public long ToInt64(object value) + { + return GetTokenValue(value); + } + + public sbyte ToSByte(object value) + { + return GetTokenValue(value); + } + + public float ToSingle(object value) + { + return GetTokenValue(value); + } + + public string ToString(object value) + { + return GetTokenValue(value); + } + + public ushort ToUInt16(object value) + { + return GetTokenValue(value); + } + + public uint ToUInt32(object value) + { + return GetTokenValue(value); + } + + public ulong ToUInt64(object value) + { + return GetTokenValue(value); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonISerializableContract.cs b/Libs/JsonNet/Serialization/JsonISerializableContract.cs new file mode 100644 index 0000000..d69e5fb --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonISerializableContract.cs @@ -0,0 +1,54 @@ +#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 !(NETFX_CORE || PORTABLE || PORTABLE40) +using System; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonISerializableContract : JsonContainerContract + { + /// + /// Gets or sets the ISerializable object constructor. + /// + /// The ISerializable object constructor. + public ObjectConstructor ISerializableCreator { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonISerializableContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Serializable; + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonLinqContract.cs b/Libs/JsonNet/Serialization/JsonLinqContract.cs new file mode 100644 index 0000000..c91b3f0 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonLinqContract.cs @@ -0,0 +1,45 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonLinqContract : JsonContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonLinqContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Linq; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonObjectContract.cs b/Libs/JsonNet/Serialization/JsonObjectContract.cs new file mode 100644 index 0000000..5eb0cb8 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonObjectContract.cs @@ -0,0 +1,198 @@ +#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.Globalization; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonObjectContract : JsonContainerContract + { + /// + /// Gets or sets the object member serialization. + /// + /// The member object serialization. + public MemberSerialization MemberSerialization { get; set; } + + /// + /// Gets or sets a value that indicates whether the object's properties are required. + /// + /// + /// A value indicating whether the object's properties are required. + /// + public Required? ItemRequired { get; set; } + + /// + /// Gets the object's properties. + /// + /// The object's properties. + public JsonPropertyCollection Properties { get; private set; } + + /// + /// Gets the constructor parameters required for any non-default constructor + /// + [Obsolete("ConstructorParameters is obsolete. Use CreatorParameters instead.")] + public JsonPropertyCollection ConstructorParameters + { + get { return CreatorParameters; } + } + + /// + /// Gets a collection of instances that define the parameters used with . + /// + public JsonPropertyCollection CreatorParameters { get; private set; } + + /// + /// Gets or sets the override constructor used to create the object. + /// This is set when a constructor is marked up using the + /// JsonConstructor attribute. + /// + /// The override constructor. + [Obsolete("OverrideConstructor is obsolete. Use OverrideCreator instead.")] + public ConstructorInfo OverrideConstructor + { + get { return _overrideConstructor; } + set + { + _overrideConstructor = value; + _overrideCreator = (value != null) ? JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(value) : null; + } + } + + /// + /// Gets or sets the parametrized constructor used to create the object. + /// + /// The parametrized constructor. + [Obsolete("ParametrizedConstructor is obsolete. Use OverrideCreator instead.")] + public ConstructorInfo ParametrizedConstructor + { + get { return _parametrizedConstructor; } + set + { + _parametrizedConstructor = value; + _parametrizedCreator = (value != null) ? JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(value) : null; + } + } + + /// + /// Gets or sets the function used to create the object. When set this function will override . + /// This function is called with a collection of arguments which are defined by the collection. + /// + /// The function used to create the object. + public ObjectConstructor OverrideCreator + { + get { return _overrideCreator; } + set + { + _overrideCreator = value; + _overrideConstructor = null; + } + } + + internal ObjectConstructor ParametrizedCreator + { + get { return _parametrizedCreator; } + } + + /// + /// Gets or sets the extension data setter. + /// + public ExtensionDataSetter ExtensionDataSetter { get; set; } + + /// + /// Gets or sets the extension data getter. + /// + public ExtensionDataGetter ExtensionDataGetter { get; set; } + + private bool? _hasRequiredOrDefaultValueProperties; + private ConstructorInfo _parametrizedConstructor; + private ConstructorInfo _overrideConstructor; + private ObjectConstructor _overrideCreator; + private ObjectConstructor _parametrizedCreator; + + internal bool HasRequiredOrDefaultValueProperties + { + get + { + if (_hasRequiredOrDefaultValueProperties == null) + { + _hasRequiredOrDefaultValueProperties = false; + + if (ItemRequired.GetValueOrDefault(Required.Default) != Required.Default) + { + _hasRequiredOrDefaultValueProperties = true; + } + else + { + foreach (JsonProperty property in Properties) + { + if (property.Required != Required.Default || ((property.DefaultValueHandling & DefaultValueHandling.Populate) == DefaultValueHandling.Populate) && property.Writable) + { + _hasRequiredOrDefaultValueProperties = true; + break; + } + } + } + } + + return _hasRequiredOrDefaultValueProperties.Value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonObjectContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Object; + + Properties = new JsonPropertyCollection(UnderlyingType); + CreatorParameters = new JsonPropertyCollection(UnderlyingType); + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) +#if !(NET20 || NET35) + [SecuritySafeCritical] +#endif + internal object GetUninitializedObject() + { + // we should never get here if the environment is not fully trusted, check just in case + if (!JsonTypeReflector.FullyTrusted) + throw new JsonException("Insufficient permissions. Creating an uninitialized '{0}' type requires full trust.".FormatWith(CultureInfo.InvariantCulture, NonNullableUnderlyingType)); + + return FormatterServices.GetUninitializedObject(NonNullableUnderlyingType); + } +#endif + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonPrimitiveContract.cs b/Libs/JsonNet/Serialization/JsonPrimitiveContract.cs new file mode 100644 index 0000000..6631dd7 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonPrimitiveContract.cs @@ -0,0 +1,51 @@ +#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 Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonPrimitiveContract : JsonContract + { + internal PrimitiveTypeCode TypeCode { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonPrimitiveContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.Primitive; + + TypeCode = ConvertUtils.GetTypeCode(underlyingType); + IsReadOnlyOrFixedSize = true; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonProperty.cs b/Libs/JsonNet/Serialization/JsonProperty.cs new file mode 100644 index 0000000..604dc34 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonProperty.cs @@ -0,0 +1,287 @@ +#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 Newtonsoft.Json.Utilities; + +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Maps a JSON property to a .NET member or constructor parameter. + /// + public class JsonProperty + { + internal Required? _required; + internal bool _hasExplicitDefaultValue; + + private object _defaultValue; + private bool _hasGeneratedDefaultValue; + private string _propertyName; + internal bool _skipPropertyNameEscape; + private Type _propertyType; + + // use to cache contract during deserialization + internal JsonContract PropertyContract { get; set; } + + /// + /// Gets or sets the name of the property. + /// + /// The name of the property. + public string PropertyName + { + get { return _propertyName; } + set + { + _propertyName = value; + _skipPropertyNameEscape = !JavaScriptUtils.ShouldEscapeJavaScriptString(_propertyName, JavaScriptUtils.HtmlCharEscapeFlags); + } + } + + /// + /// Gets or sets the type that declared this property. + /// + /// The type that declared this property. + public Type DeclaringType { get; set; } + + /// + /// Gets or sets the order of serialization and deserialization of a member. + /// + /// The numeric order of serialization or deserialization. + public int? Order { get; set; } + + /// + /// Gets or sets the name of the underlying member or parameter. + /// + /// The name of the underlying member or parameter. + public string UnderlyingName { get; set; } + + /// + /// Gets the that will get and set the during serialization. + /// + /// The that will get and set the during serialization. + public IValueProvider ValueProvider { get; set; } + + /// + /// Gets or sets the type of the property. + /// + /// The type of the property. + public Type PropertyType + { + get { return _propertyType; } + set + { + if (_propertyType != value) + { + _propertyType = value; + _hasGeneratedDefaultValue = false; + } + } + } + + /// + /// Gets or sets the for the property. + /// If set this converter takes presidence over the contract converter for the property type. + /// + /// The converter. + public JsonConverter Converter { get; set; } + + /// + /// Gets or sets the member converter. + /// + /// The member converter. + public JsonConverter MemberConverter { get; set; } + + /// + /// Gets or sets a value indicating whether this is ignored. + /// + /// true if ignored; otherwise, false. + public bool Ignored { get; set; } + + /// + /// Gets or sets a value indicating whether this is readable. + /// + /// true if readable; otherwise, false. + public bool Readable { get; set; } + + /// + /// Gets or sets a value indicating whether this is writable. + /// + /// true if writable; otherwise, false. + public bool Writable { get; set; } + + /// + /// Gets or sets a value indicating whether this has a member attribute. + /// + /// true if has a member attribute; otherwise, false. + public bool HasMemberAttribute { get; set; } + + /// + /// Gets the default value. + /// + /// The default value. + public object DefaultValue + { + get + { + if (!_hasExplicitDefaultValue) + return null; + + return _defaultValue; + } + set + { + _hasExplicitDefaultValue = true; + _defaultValue = value; + } + } + + internal object GetResolvedDefaultValue() + { + if (_propertyType == null) + return null; + + if (!_hasExplicitDefaultValue && !_hasGeneratedDefaultValue) + { + _defaultValue = ReflectionUtils.GetDefaultValue(PropertyType); + _hasGeneratedDefaultValue = true; + } + + return _defaultValue; + } + + /// + /// Gets or sets a value indicating whether this is required. + /// + /// A value indicating whether this is required. + public Required Required + { + get { return _required ?? Required.Default; } + set { _required = value; } + } + + /// + /// Gets or sets a value indicating whether this property preserves object references. + /// + /// + /// true if this instance is reference; otherwise, false. + /// + public bool? IsReference { get; set; } + + /// + /// Gets or sets the property null value handling. + /// + /// The null value handling. + public NullValueHandling? NullValueHandling { get; set; } + + /// + /// Gets or sets the property default value handling. + /// + /// The default value handling. + public DefaultValueHandling? DefaultValueHandling { get; set; } + + /// + /// Gets or sets the property reference loop handling. + /// + /// The reference loop handling. + public ReferenceLoopHandling? ReferenceLoopHandling { get; set; } + + /// + /// Gets or sets the property object creation handling. + /// + /// The object creation handling. + public ObjectCreationHandling? ObjectCreationHandling { get; set; } + + /// + /// Gets or sets or sets the type name handling. + /// + /// The type name handling. + public TypeNameHandling? TypeNameHandling { get; set; } + + /// + /// Gets or sets a predicate used to determine whether the property should be serialize. + /// + /// A predicate used to determine whether the property should be serialize. + public Predicate ShouldSerialize { get; set; } + + /// + /// Gets or sets a predicate used to determine whether the property should be serialized. + /// + /// A predicate used to determine whether the property should be serialized. + public Predicate GetIsSpecified { get; set; } + + /// + /// Gets or sets an action used to set whether the property has been deserialized. + /// + /// An action used to set whether the property has been deserialized. + public Action SetIsSpecified { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return PropertyName; + } + + /// + /// Gets or sets the converter used when serializing the property's collection items. + /// + /// The collection's items converter. + public JsonConverter ItemConverter { get; set; } + + /// + /// Gets or sets whether this property's collection items are serialized as a reference. + /// + /// Whether this property's collection items are serialized as a reference. + public bool? ItemIsReference { get; set; } + + /// + /// Gets or sets the the type name handling used when serializing the property's collection items. + /// + /// The collection's items type name handling. + public TypeNameHandling? ItemTypeNameHandling { get; set; } + + /// + /// Gets or sets the the reference loop handling used when serializing the property's collection items. + /// + /// The collection's items reference loop handling. + public ReferenceLoopHandling? ItemReferenceLoopHandling { get; set; } + + internal void WritePropertyName(JsonWriter writer) + { + if (_skipPropertyNameEscape) + writer.WritePropertyName(PropertyName, false); + else + writer.WritePropertyName(PropertyName); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonPropertyCollection.cs b/Libs/JsonNet/Serialization/JsonPropertyCollection.cs new file mode 100644 index 0000000..406f4b8 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonPropertyCollection.cs @@ -0,0 +1,165 @@ +#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; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// A collection of objects. + /// + public class JsonPropertyCollection : KeyedCollection + { + private readonly Type _type; + + /// + /// Initializes a new instance of the class. + /// + /// The type. + public JsonPropertyCollection(Type type) + : base(StringComparer.Ordinal) + { + ValidationUtils.ArgumentNotNull(type, "type"); + _type = type; + } + + /// + /// When implemented in a derived class, extracts the key from the specified element. + /// + /// The element from which to extract the key. + /// The key for the specified element. + protected override string GetKeyForItem(JsonProperty item) + { + return item.PropertyName; + } + + /// + /// Adds a object. + /// + /// The property to add to the collection. + public void AddProperty(JsonProperty property) + { + if (Contains(property.PropertyName)) + { + // don't overwrite existing property with ignored property + if (property.Ignored) + return; + + JsonProperty existingProperty = this[property.PropertyName]; + bool duplicateProperty = true; + + if (existingProperty.Ignored) + { + // remove ignored property so it can be replaced in collection + Remove(existingProperty); + duplicateProperty = false; + } + else + { + if (property.DeclaringType != null && existingProperty.DeclaringType != null) + { + if (property.DeclaringType.IsSubclassOf(existingProperty.DeclaringType)) + { + // current property is on a derived class and hides the existing + Remove(existingProperty); + duplicateProperty = false; + } + if (existingProperty.DeclaringType.IsSubclassOf(property.DeclaringType)) + { + // current property is hidden by the existing so don't add it + return; + } + } + } + + if (duplicateProperty) + throw new JsonSerializationException("A member with the name '{0}' already exists on '{1}'. Use the JsonPropertyAttribute to specify another name.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, _type)); + } + + Add(property); + } + + /// + /// Gets the closest matching object. + /// First attempts to get an exact case match of propertyName and then + /// a case insensitive match. + /// + /// Name of the property. + /// A matching property if found. + public JsonProperty GetClosestMatchProperty(string propertyName) + { + JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal); + if (property == null) + property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase); + + return property; + } + + private bool TryGetValue(string key, out JsonProperty item) + { + if (Dictionary == null) + { + item = default(JsonProperty); + return false; + } + + return Dictionary.TryGetValue(key, out item); + } + + /// + /// Gets a property by property name. + /// + /// The name of the property to get. + /// Type property name string comparison. + /// A matching property if found. + public JsonProperty GetProperty(string propertyName, StringComparison comparisonType) + { + // KeyedCollection has an ordinal comparer + if (comparisonType == StringComparison.Ordinal) + { + JsonProperty property; + if (TryGetValue(propertyName, out property)) + return property; + + return null; + } + + foreach (JsonProperty property in this) + { + if (string.Equals(propertyName, property.PropertyName, comparisonType)) + { + return property; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonSerializerInternalBase.cs b/Libs/JsonNet/Serialization/JsonSerializerInternalBase.cs new file mode 100644 index 0000000..b49b662 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonSerializerInternalBase.cs @@ -0,0 +1,141 @@ +#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.Diagnostics; +using System.Net; +using System.Runtime.CompilerServices; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal abstract class JsonSerializerInternalBase + { + private class ReferenceEqualsEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + int IEqualityComparer.GetHashCode(object obj) + { +#if !(NETFX_CORE) + // put objects in a bucket based on their reference + return RuntimeHelpers.GetHashCode(obj); +#else + // put all objects in the same bucket so ReferenceEquals is called on all + return -1; +#endif + } + } + + private ErrorContext _currentErrorContext; + private BidirectionalDictionary _mappings; + private bool _serializing; + + internal readonly JsonSerializer Serializer; + internal readonly ITraceWriter TraceWriter; + + protected JsonSerializerInternalBase(JsonSerializer serializer) + { + ValidationUtils.ArgumentNotNull(serializer, "serializer"); + + Serializer = serializer; + TraceWriter = serializer.TraceWriter; + + // kind of a hack but meh. might clean this up later + _serializing = (GetType() == typeof(JsonSerializerInternalWriter)); + } + + internal BidirectionalDictionary DefaultReferenceMappings + { + get + { + // override equality comparer for object key dictionary + // object will be modified as it deserializes and might have mutable hashcode + if (_mappings == null) + _mappings = new BidirectionalDictionary( + EqualityComparer.Default, + new ReferenceEqualsEqualityComparer(), + "A different value already has the Id '{0}'.", + "A different Id has already been assigned for value '{0}'."); + + return _mappings; + } + } + + private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error) + { + if (_currentErrorContext == null) + _currentErrorContext = new ErrorContext(currentObject, member, path, error); + + if (_currentErrorContext.Error != error) + throw new InvalidOperationException("Current error context error is different to requested error."); + + return _currentErrorContext; + } + + protected void ClearErrorContext() + { + if (_currentErrorContext == null) + throw new InvalidOperationException("Could not clear error context. Error context is already null."); + + _currentErrorContext = null; + } + + protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, IJsonLineInfo lineInfo, string path, Exception ex) + { + ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Error && !errorContext.Traced) + { + // only write error once + errorContext.Traced = true; + + string message = (_serializing) ? "Error serializing" : "Error deserializing"; + if (contract != null) + message += " " + contract.UnderlyingType; + message += ". " + ex.Message; + + // add line information to non-json.net exception message + if (!(ex is JsonException)) + message = JsonPosition.FormatMessage(lineInfo, path, message); + + TraceWriter.Trace(TraceLevel.Error, message, ex); + } + + // attribute method is non-static so don't invoke if no object + if (contract != null && currentObject != null) + contract.InvokeOnError(currentObject, Serializer.Context, errorContext); + + if (!errorContext.Handled) + Serializer.OnError(new ErrorEventArgs(currentObject, errorContext)); + + return errorContext.Handled; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonSerializerInternalReader.cs b/Libs/JsonNet/Serialization/JsonSerializerInternalReader.cs new file mode 100644 index 0000000..e90c7e4 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonSerializerInternalReader.cs @@ -0,0 +1,2110 @@ +#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; +using System.Collections.Generic; +using System.Collections.ObjectModel; +#if !(NET35 || NET20 || PORTABLE40) +using System.ComponentModel; +using System.Dynamic; +#endif +using System.Diagnostics; +using System.Globalization; +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) +using System.Numerics; +#endif +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerInternalReader : JsonSerializerInternalBase + { + internal enum PropertyPresence + { + None, + Null, + Value + } + + private JsonSerializerProxy _internalSerializer; + + public JsonSerializerInternalReader(JsonSerializer serializer) + : base(serializer) + { + } + + public void Populate(JsonReader reader, object target) + { + ValidationUtils.ArgumentNotNull(target, "target"); + + Type objectType = target.GetType(); + + JsonContract contract = Serializer._contractResolver.ResolveContract(objectType); + + if (reader.TokenType == JsonToken.None) + reader.Read(); + + if (reader.TokenType == JsonToken.StartArray) + { + if (contract.ContractType == JsonContractType.Array) + { + JsonArrayContract arrayContract = (JsonArrayContract)contract; + + PopulateList((arrayContract.ShouldCreateWrapper) ? arrayContract.CreateWrapper(target) : (IList)target, reader, arrayContract, null, null); + } + else + { + throw JsonSerializationException.Create(reader, "Cannot populate JSON array onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + } + else if (reader.TokenType == JsonToken.StartObject) + { + CheckedRead(reader); + + string id = null; + if (Serializer.MetadataPropertyHandling != MetadataPropertyHandling.Ignore + && reader.TokenType == JsonToken.PropertyName + && string.Equals(reader.Value.ToString(), JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + id = (reader.Value != null) ? reader.Value.ToString() : null; + CheckedRead(reader); + } + + if (contract.ContractType == JsonContractType.Dictionary) + { + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)contract; + PopulateDictionary((dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(target) : (IDictionary)target, reader, dictionaryContract, null, id); + } + else if (contract.ContractType == JsonContractType.Object) + { + PopulateObject(target, reader, (JsonObjectContract)contract, null, id); + } + else + { + throw JsonSerializationException.Create(reader, "Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + } + else + { + throw JsonSerializationException.Create(reader, "Unexpected initial token '{0}' when populating object. Expected JSON object or array.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private JsonContract GetContractSafe(Type type) + { + if (type == null) + return null; + + return Serializer._contractResolver.ResolveContract(type); + } + + public object Deserialize(JsonReader reader, Type objectType, bool checkAdditionalContent) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + JsonContract contract = GetContractSafe(objectType); + + try + { + JsonConverter converter = GetConverter(contract, null, null, null); + + if (reader.TokenType == JsonToken.None && !ReadForType(reader, contract, converter != null)) + { + if (contract != null && !contract.IsNullable) + throw JsonSerializationException.Create(reader, "No JSON content found and type '{0}' is not nullable.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + return null; + } + + object deserializedValue; + + if (converter != null && converter.CanRead) + deserializedValue = DeserializeConvertable(converter, reader, objectType, null); + else + deserializedValue = CreateValueInternal(reader, objectType, contract, null, null, null, null); + + if (checkAdditionalContent) + { + if (reader.Read() && reader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + } + + return deserializedValue; + } + catch (Exception ex) + { + if (IsErrorHandled(null, contract, null, reader as IJsonLineInfo, reader.Path, ex)) + { + HandleError(reader, false, 0); + return null; + } + else + { + // clear context in case serializer is being used inside a converter + // if the converter wraps the error then not clearing the context will cause this error: + // "Current error context error is different to requested error." + ClearErrorContext(); + throw; + } + } + } + + private JsonSerializerProxy GetInternalSerializer() + { + if (_internalSerializer == null) + _internalSerializer = new JsonSerializerProxy(this); + + return _internalSerializer; + } + + private JToken CreateJToken(JsonReader reader, JsonContract contract) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (contract != null) + { + if (contract.UnderlyingType == typeof(JRaw)) + return JRaw.Create(reader); + if (reader.TokenType == JsonToken.Null + && !(contract.UnderlyingType == typeof(JValue) || contract.UnderlyingType == typeof(JToken))) + return null; + } + + JToken token; + using (JTokenWriter writer = new JTokenWriter()) + { + writer.WriteToken(reader); + token = writer.Token; + } + + return token; + } + + private JToken CreateJObject(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + // this is needed because we've already read inside the object, looking for metadata properties + using (JTokenWriter writer = new JTokenWriter()) + { + writer.WriteStartObject(); + + do + { + if (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = (string)reader.Value; + do + { + if (!reader.Read()) + break; + } while (reader.TokenType == JsonToken.Comment); + + if (CheckPropertyName(reader, propertyName)) + continue; + + writer.WritePropertyName(propertyName); + writer.WriteToken(reader, true, true); + } + else if (reader.TokenType == JsonToken.Comment) + { + // eat + } + else + { + writer.WriteEndObject(); + return writer.Token; + } + } while (reader.Read()); + + throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + } + } + + private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue) + { + if (contract != null && contract.ContractType == JsonContractType.Linq) + return CreateJToken(reader, contract); + + do + { + switch (reader.TokenType) + { + // populate a typed object or generic dictionary/array + // depending upon whether an objectType was supplied + case JsonToken.StartObject: + return CreateObject(reader, objectType, contract, member, containerContract, containerMember, existingValue); + case JsonToken.StartArray: + return CreateList(reader, objectType, contract, member, existingValue, null); + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Boolean: + case JsonToken.Date: + case JsonToken.Bytes: + return EnsureType(reader, reader.Value, CultureInfo.InvariantCulture, contract, objectType); + case JsonToken.String: + string s = (string)reader.Value; + + // convert empty string to null automatically for nullable types + if (string.IsNullOrEmpty(s) && objectType != typeof(string) && objectType != typeof(object) && contract != null && contract.IsNullable) + return null; + + // string that needs to be returned as a byte array should be base 64 decoded + if (objectType == typeof(byte[])) + return Convert.FromBase64String(s); + + return EnsureType(reader, s, CultureInfo.InvariantCulture, contract, objectType); + case JsonToken.StartConstructor: + string constructorName = reader.Value.ToString(); + + return EnsureType(reader, constructorName, CultureInfo.InvariantCulture, contract, objectType); + case JsonToken.Null: + case JsonToken.Undefined: +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (objectType == typeof(DBNull)) + return DBNull.Value; +#endif + + return EnsureType(reader, reader.Value, CultureInfo.InvariantCulture, contract, objectType); + case JsonToken.Raw: + return new JRaw((string)reader.Value); + case JsonToken.Comment: + // ignore + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token while deserializing object: " + reader.TokenType); + } + } while (reader.Read()); + + throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + } + + internal string GetExpectedDescription(JsonContract contract) + { + switch (contract.ContractType) + { + case JsonContractType.Object: + case JsonContractType.Dictionary: +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + case JsonContractType.Serializable: +#endif +#if !(NET35 || NET20 || PORTABLE40) + case JsonContractType.Dynamic: +#endif + return @"JSON object (e.g. {""name"":""value""})"; + case JsonContractType.Array: + return @"JSON array (e.g. [1,2,3])"; + case JsonContractType.Primitive: + return @"JSON primitive value (e.g. string, number, boolean, null)"; + case JsonContractType.String: + return @"JSON string value"; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private JsonConverter GetConverter(JsonContract contract, JsonConverter memberConverter, JsonContainerContract containerContract, JsonProperty containerProperty) + { + JsonConverter converter = null; + if (memberConverter != null) + { + // member attribute converter + converter = memberConverter; + } + else if (containerProperty != null && containerProperty.ItemConverter != null) + { + converter = containerProperty.ItemConverter; + } + else if (containerContract != null && containerContract.ItemConverter != null) + { + converter = containerContract.ItemConverter; + } + else if (contract != null) + { + JsonConverter matchingConverter; + if (contract.Converter != null) + // class attribute converter + converter = contract.Converter; + else if ((matchingConverter = Serializer.GetMatchingConverter(contract.UnderlyingType)) != null) + // passed in converters + converter = matchingConverter; + else if (contract.InternalConverter != null) + // internally specified converter + converter = contract.InternalConverter; + } + return converter; + } + + private object CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue) + { + string id; + Type resolvedObjectType = objectType; + + if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.Ignore) + { + // don't look for metadata properties + CheckedRead(reader); + id = null; + } + else if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.ReadAhead) + { + var tokenReader = reader as JTokenReader; + if (tokenReader == null) + { + JToken t = JToken.ReadFrom(reader); + tokenReader = (JTokenReader)t.CreateReader(); + tokenReader.Culture = reader.Culture; + tokenReader.DateFormatString = reader.DateFormatString; + tokenReader.DateParseHandling = reader.DateParseHandling; + tokenReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; + tokenReader.FloatParseHandling = reader.FloatParseHandling; + tokenReader.SupportMultipleContent = reader.SupportMultipleContent; + + // start + CheckedRead(tokenReader); + + reader = tokenReader; + } + + object newValue; + if (ReadMetadataPropertiesToken(tokenReader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + return newValue; + } + else + { + CheckedRead(reader); + object newValue; + if (ReadMetadataProperties(reader, ref resolvedObjectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id)) + return newValue; + } + + if (HasNoDefinedType(contract)) + return CreateJObject(reader); + + switch (contract.ContractType) + { + case JsonContractType.Object: + { + bool createdFromNonDefaultCreator = false; + JsonObjectContract objectContract = (JsonObjectContract)contract; + object targetObject; + // check that if type name handling is being used that the existing value is compatible with the specified type + if (existingValue != null && (resolvedObjectType == objectType || resolvedObjectType.IsAssignableFrom(existingValue.GetType()))) + targetObject = existingValue; + else + targetObject = CreateNewObject(reader, objectContract, member, containerMember, id, out createdFromNonDefaultCreator); + + // don't populate if read from non-default creator because the object has already been read + if (createdFromNonDefaultCreator) + return targetObject; + + return PopulateObject(targetObject, reader, objectContract, member, id); + } + case JsonContractType.Primitive: + { + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; + // if the content is inside $value then read past it + if (Serializer.MetadataPropertyHandling != MetadataPropertyHandling.Ignore + && reader.TokenType == JsonToken.PropertyName + && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + + // the token should not be an object because the $type value could have been included in the object + // without needing the $value property + if (reader.TokenType == JsonToken.StartObject) + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing primitive value: " + reader.TokenType); + + object value = CreateValueInternal(reader, resolvedObjectType, primitiveContract, member, null, null, existingValue); + + CheckedRead(reader); + return value; + } + break; + } + case JsonContractType.Dictionary: + { + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)contract; + object targetDictionary; + + if (existingValue == null) + { + bool createdFromNonDefaultCreator; + IDictionary dictionary = CreateNewDictionary(reader, dictionaryContract, out createdFromNonDefaultCreator); + + if (createdFromNonDefaultCreator) + { + if (id != null) + throw JsonSerializationException.Create(reader, "Cannot preserve reference to readonly dictionary, or dictionary created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.OnSerializingCallbacks.Count > 0) + throw JsonSerializationException.Create(reader, "Cannot call OnSerializing on readonly dictionary, or dictionary created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.OnErrorCallbacks.Count > 0) + throw JsonSerializationException.Create(reader, "Cannot call OnError on readonly list, or dictionary created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (!dictionaryContract.HasParametrizedCreator) + throw JsonSerializationException.Create(reader, "Cannot deserialize readonly or fixed size dictionary: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + } + + PopulateDictionary(dictionary, reader, dictionaryContract, member, id); + + if (createdFromNonDefaultCreator) + { + return dictionaryContract.ParametrizedCreator(dictionary); + } + else if (dictionary is IWrappedDictionary) + { + return ((IWrappedDictionary)dictionary).UnderlyingDictionary; + } + + targetDictionary = dictionary; + } + else + { + targetDictionary = PopulateDictionary(dictionaryContract.ShouldCreateWrapper ? dictionaryContract.CreateWrapper(existingValue) : (IDictionary)existingValue, reader, dictionaryContract, member, id); + } + + return targetDictionary; + } +#if !(NET35 || NET20 || PORTABLE40) + case JsonContractType.Dynamic: + JsonDynamicContract dynamicContract = (JsonDynamicContract)contract; + return CreateDynamic(reader, dynamicContract, member, id); +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + case JsonContractType.Serializable: + JsonISerializableContract serializableContract = (JsonISerializableContract)contract; + return CreateISerializable(reader, serializableContract, member, id); +#endif + } + + string message = @"Cannot deserialize the current JSON object (e.g. {{""name"":""value""}}) into type '{0}' because the type requires a {1} to deserialize correctly." + Environment.NewLine + + @"To fix this error either change the JSON to a {1} or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object." + Environment.NewLine; + message = message.FormatWith(CultureInfo.InvariantCulture, resolvedObjectType, GetExpectedDescription(contract)); + + throw JsonSerializationException.Create(reader, message); + } + + private bool ReadMetadataPropertiesToken(JTokenReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue, out object newValue, out string id) + { + id = null; + newValue = null; + + if (reader.TokenType == JsonToken.StartObject) + { + JObject current = (JObject)reader._current; + + JToken refToken = current[JsonTypeReflector.RefPropertyName]; + if (refToken != null) + { + if (refToken.Type != JTokenType.String && refToken.Type != JTokenType.Null) + throw JsonSerializationException.Create(refToken, refToken.Path, "JSON reference {0} property must have a string or null value.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName), null); + + JToken property = refToken.Parent; + JToken additionalContent = null; + if (property.Next != null) + additionalContent = property.Next; + else if (property.Previous != null) + additionalContent = property.Previous; + + string reference = (string)refToken; + + if (reference != null) + { + if (additionalContent != null) + throw JsonSerializationException.Create(additionalContent, additionalContent.Path, "Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName), null); + + newValue = Serializer.GetReferenceResolver().ResolveReference(this, reference); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null); + + reader.Skip(); + return true; + } + } + JToken typeToken = current[JsonTypeReflector.TypePropertyName]; + if (typeToken != null) + { + string qualifiedTypeName = (string)typeToken; + JsonReader typeTokenReader = typeToken.CreateReader(); + CheckedRead(typeTokenReader); + ResolveTypeName(typeTokenReader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName); + + JToken valueToken = current[JsonTypeReflector.ValuePropertyName]; + if (valueToken != null) + { + while (true) + { + CheckedRead(reader); + if (reader.TokenType == JsonToken.PropertyName) + { + if ((string)reader.Value == JsonTypeReflector.ValuePropertyName) + return false; + } + + CheckedRead(reader); + reader.Skip(); + } + } + } + JToken idToken = current[JsonTypeReflector.IdPropertyName]; + if (idToken != null) + { + id = (string)idToken; + } + JToken valuesToken = current[JsonTypeReflector.ArrayValuesPropertyName]; + if (valuesToken != null) + { + JsonReader listReader = valuesToken.CreateReader(); + CheckedRead(listReader); + newValue = CreateList(listReader, objectType, contract, member, existingValue, id); + + reader.Skip(); + return true; + } + } + + CheckedRead(reader); + return false; + } + + private bool ReadMetadataProperties(JsonReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue, out object newValue, out string id) + { + id = null; + newValue = null; + + if (reader.TokenType == JsonToken.PropertyName) + { + string propertyName = reader.Value.ToString(); + + if (propertyName.Length > 0 && propertyName[0] == '$') + { + // read metadata properties + // $type, $id, $ref, etc + bool metadataProperty; + + do + { + propertyName = reader.Value.ToString(); + + if (string.Equals(propertyName, JsonTypeReflector.RefPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + if (reader.TokenType != JsonToken.String && reader.TokenType != JsonToken.Null) + throw JsonSerializationException.Create(reader, "JSON reference {0} property must have a string or null value.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); + + string reference = (reader.Value != null) ? reader.Value.ToString() : null; + + CheckedRead(reader); + + if (reference != null) + { + if (reader.TokenType == JsonToken.PropertyName) + throw JsonSerializationException.Create(reader, "Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); + + newValue = Serializer.GetReferenceResolver().ResolveReference(this, reference); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null); + + return true; + } + else + { + metadataProperty = true; + } + } + else if (string.Equals(propertyName, JsonTypeReflector.TypePropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + string qualifiedTypeName = reader.Value.ToString(); + + ResolveTypeName(reader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName); + + CheckedRead(reader); + + metadataProperty = true; + } + else if (string.Equals(propertyName, JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + + id = (reader.Value != null) ? reader.Value.ToString() : null; + + CheckedRead(reader); + metadataProperty = true; + } + else if (string.Equals(propertyName, JsonTypeReflector.ArrayValuesPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + object list = CreateList(reader, objectType, contract, member, existingValue, id); + CheckedRead(reader); + newValue = list; + return true; + } + else + { + metadataProperty = false; + } + } while (metadataProperty && reader.TokenType == JsonToken.PropertyName); + } + } + return false; + } + + private void ResolveTypeName(JsonReader reader, ref Type objectType, ref JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, string qualifiedTypeName) + { + TypeNameHandling resolvedTypeNameHandling = + ((member != null) ? member.TypeNameHandling : null) + ?? ((containerContract != null) ? containerContract.ItemTypeNameHandling : null) + ?? ((containerMember != null) ? containerMember.ItemTypeNameHandling : null) + ?? Serializer._typeNameHandling; + + if (resolvedTypeNameHandling != TypeNameHandling.None) + { + string typeName; + string assemblyName; + ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName, out typeName, out assemblyName); + + Type specifiedType; + try + { + specifiedType = Serializer._binder.BindToType(assemblyName, typeName); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error resolving type specified in JSON '{0}'.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName), ex); + } + + if (specifiedType == null) + throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved type '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName, specifiedType)), null); + + if (objectType != null +#if !(NET35 || NET20 || PORTABLE40) + && objectType != typeof(IDynamicMetaObjectProvider) +#endif + && !objectType.IsAssignableFrom(specifiedType)) + throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); + + objectType = specifiedType; + contract = GetContractSafe(specifiedType); + } + } + + private JsonArrayContract EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract) + { + if (contract == null) + throw JsonSerializationException.Create(reader, "Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + JsonArrayContract arrayContract = contract as JsonArrayContract; + if (arrayContract == null) + { + string message = @"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type '{0}' because the type requires a {1} to deserialize correctly." + Environment.NewLine + + @"To fix this error either change the JSON to a {1} or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array." + Environment.NewLine; + message = message.FormatWith(CultureInfo.InvariantCulture, objectType, GetExpectedDescription(contract)); + + throw JsonSerializationException.Create(reader, message); + } + + return arrayContract; + } + + private void CheckedRead(JsonReader reader) + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + } + + private object CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue, string id) + { + object value; + + if (HasNoDefinedType(contract)) + return CreateJToken(reader, contract); + + JsonArrayContract arrayContract = EnsureArrayContract(reader, objectType, contract); + + if (existingValue == null) + { + bool createdFromNonDefaultCreator; + IList list = CreateNewList(reader, arrayContract, out createdFromNonDefaultCreator); + + if (createdFromNonDefaultCreator) + { + if (id != null) + throw JsonSerializationException.Create(reader, "Cannot preserve reference to array or readonly list, or list created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.OnSerializingCallbacks.Count > 0) + throw JsonSerializationException.Create(reader, "Cannot call OnSerializing on an array or readonly list, or list created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.OnErrorCallbacks.Count > 0) + throw JsonSerializationException.Create(reader, "Cannot call OnError on an array or readonly list, or list created from a non-default constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (!arrayContract.HasParametrizedCreator && !arrayContract.IsArray) + throw JsonSerializationException.Create(reader, "Cannot deserialize readonly or fixed size list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + } + + if (!arrayContract.IsMultidimensionalArray) + PopulateList(list, reader, arrayContract, member, id); + else + PopulateMultidimensionalArray(list, reader, arrayContract, member, id); + + if (createdFromNonDefaultCreator) + { + if (arrayContract.IsMultidimensionalArray) + { + list = CollectionUtils.ToMultidimensionalArray(list, arrayContract.CollectionItemType, contract.CreatedType.GetArrayRank()); + } + else if (arrayContract.IsArray) + { + Array a = Array.CreateInstance(arrayContract.CollectionItemType, list.Count); + list.CopyTo(a, 0); + list = a; + } + else + { + // call constructor that takes IEnumerable + return arrayContract.ParametrizedCreator(list); + } + } + else if (list is IWrappedCollection) + { + return ((IWrappedCollection)list).UnderlyingCollection; + } + + value = list; + } + else + { + value = PopulateList((arrayContract.ShouldCreateWrapper) ? arrayContract.CreateWrapper(existingValue) : (IList)existingValue, reader, arrayContract, member, id); + } + + return value; + } + + private bool HasNoDefinedType(JsonContract contract) + { + return (contract == null || contract.UnderlyingType == typeof(object) || contract.ContractType == JsonContractType.Linq +#if !(NET35 || NET20 || PORTABLE40) + || contract.UnderlyingType == typeof(IDynamicMetaObjectProvider) +#endif + ); + } + + private object EnsureType(JsonReader reader, object value, CultureInfo culture, JsonContract contract, Type targetType) + { + if (targetType == null) + return value; + + Type valueType = ReflectionUtils.GetObjectType(value); + + // type of value and type of target don't match + // attempt to convert value's type to target's type + if (valueType != targetType) + { + if (value == null && contract.IsNullable) + return null; + + try + { + if (contract.IsConvertable) + { + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; + + if (contract.IsEnum) + { + if (value is string) + return Enum.Parse(contract.NonNullableUnderlyingType, value.ToString(), true); + if (ConvertUtils.IsInteger(primitiveContract.TypeCode)) + return Enum.ToObject(contract.NonNullableUnderlyingType, value); + } + +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) + if (value is BigInteger) + return ConvertUtils.FromBigInteger((BigInteger)value, targetType); +#endif + + // this won't work when converting to a custom IConvertible + return Convert.ChangeType(value, contract.NonNullableUnderlyingType, culture); + } + + return ConvertUtils.ConvertOrCast(value, culture, contract.NonNullableUnderlyingType); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.FormatValueForPrint(value), targetType), ex); + } + } + + return value; + } + + private bool SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, object target) + { + object currentValue; + bool useExistingValue; + JsonContract propertyContract; + bool gottenCurrentValue; + + if (CalculatePropertyDetails(property, ref propertyConverter, containerContract, containerProperty, reader, target, out useExistingValue, out currentValue, out propertyContract, out gottenCurrentValue)) + return false; + + object value; + + if (propertyConverter != null && propertyConverter.CanRead) + { + if (!gottenCurrentValue && target != null && property.Readable) + currentValue = property.ValueProvider.GetValue(target); + + value = DeserializeConvertable(propertyConverter, reader, property.PropertyType, currentValue); + } + else + { + value = CreateValueInternal(reader, property.PropertyType, propertyContract, property, containerContract, containerProperty, (useExistingValue) ? currentValue : null); + } + + // always set the value if useExistingValue is false, + // otherwise also set it if CreateValue returns a new value compared to the currentValue + // this could happen because of a JsonConverter against the type + if ((!useExistingValue || value != currentValue) + && ShouldSetPropertyValue(property, value)) + { + property.ValueProvider.SetValue(target, value); + + if (property.SetIsSpecified != null) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "IsSpecified for property '{0}' on {1} set to true.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, property.DeclaringType)), null); + + property.SetIsSpecified(target, true); + } + + return true; + } + + // the value wasn't set be JSON was populated onto the existing value + return useExistingValue; + } + + private bool CalculatePropertyDetails(JsonProperty property, ref JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, object target, out bool useExistingValue, out object currentValue, out JsonContract propertyContract, out bool gottenCurrentValue) + { + currentValue = null; + useExistingValue = false; + propertyContract = null; + gottenCurrentValue = false; + + if (property.Ignored) + return true; + + JsonToken tokenType = reader.TokenType; + + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + ObjectCreationHandling objectCreationHandling = + property.ObjectCreationHandling.GetValueOrDefault(Serializer._objectCreationHandling); + + if ((objectCreationHandling != ObjectCreationHandling.Replace) + && (tokenType == JsonToken.StartArray || tokenType == JsonToken.StartObject) + && property.Readable) + { + currentValue = property.ValueProvider.GetValue(target); + gottenCurrentValue = true; + + if (currentValue != null) + { + propertyContract = GetContractSafe(currentValue.GetType()); + + useExistingValue = (!propertyContract.IsReadOnlyOrFixedSize && !propertyContract.UnderlyingType.IsValueType()); + } + } + + if (!property.Writable && !useExistingValue) + return true; + + // test tokentype here because null might not be convertable to some types, e.g. ignoring null when applied to DateTime + if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore && tokenType == JsonToken.Null) + return true; + + // test tokentype here because default value might not be convertable to actual type, e.g. default of "" for DateTime + if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Ignore) + && !HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate) + && JsonReader.IsPrimitiveToken(tokenType) + && MiscellaneousUtils.ValueEquals(reader.Value, property.GetResolvedDefaultValue())) + return true; + + if (currentValue == null) + { + propertyContract = property.PropertyContract; + } + else + { + propertyContract = GetContractSafe(currentValue.GetType()); + + if (propertyContract != property.PropertyContract) + propertyConverter = GetConverter(propertyContract, property.MemberConverter, containerContract, containerProperty); + } + + return false; + } + + private void AddReference(JsonReader reader, string id, object value) + { + try + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Read object reference Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, id, value.GetType())), null); + + Serializer.GetReferenceResolver().AddReference(this, id, value); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error reading object reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, id), ex); + } + } + + private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag) + { + return ((value & flag) == flag); + } + + private bool ShouldSetPropertyValue(JsonProperty property, object value) + { + if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore && value == null) + return false; + + if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Ignore) + && !HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate) + && MiscellaneousUtils.ValueEquals(value, property.GetResolvedDefaultValue())) + return false; + + if (!property.Writable) + return false; + + return true; + } + + private IList CreateNewList(JsonReader reader, JsonArrayContract contract, out bool createdFromNonDefaultCreator) + { + // some types like non-generic IEnumerable can be serialized but not deserialized + if (!contract.CanDeserialize) + throw JsonSerializationException.Create(reader, "Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.CreatedType)); + + if (contract.IsReadOnlyOrFixedSize) + { + createdFromNonDefaultCreator = true; + IList list = contract.CreateTemporaryCollection(); + + if (contract.ShouldCreateWrapper) + list = contract.CreateWrapper(list); + + return list; + } + else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + { + object list = contract.DefaultCreator(); + + if (contract.ShouldCreateWrapper) + list = contract.CreateWrapper(list); + + createdFromNonDefaultCreator = false; + return (IList)list; + } + else if (contract.HasParametrizedCreator) + { + createdFromNonDefaultCreator = true; + return contract.CreateTemporaryCollection(); + } + else + { + if (!contract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + throw JsonSerializationException.Create(reader, "Unable to find a constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + } + } + + private IDictionary CreateNewDictionary(JsonReader reader, JsonDictionaryContract contract, out bool createdFromNonDefaultCreator) + { + if (contract.IsReadOnlyOrFixedSize) + { + createdFromNonDefaultCreator = true; + return contract.CreateTemporaryDictionary(); + } + else if (contract.DefaultCreator != null && (!contract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + { + object dictionary = contract.DefaultCreator(); + + if (contract.ShouldCreateWrapper) + dictionary = contract.CreateWrapper(dictionary); + + createdFromNonDefaultCreator = false; + return (IDictionary)dictionary; + } + else if (contract.HasParametrizedCreator) + { + createdFromNonDefaultCreator = true; + return contract.CreateTemporaryDictionary(); + } + else + { + if (!contract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + throw JsonSerializationException.Create(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + } + } + + private void OnDeserializing(JsonReader reader, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Started deserializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnDeserializing(value, Serializer._context); + } + + private void OnDeserialized(JsonReader reader, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Finished deserializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnDeserialized(value, Serializer._context); + } + + private object PopulateDictionary(IDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, string id) + { + IWrappedDictionary wrappedDictionary = dictionary as IWrappedDictionary; + object underlyingDictionary = wrappedDictionary != null ? wrappedDictionary.UnderlyingDictionary : dictionary; + + if (id != null) + AddReference(reader, id, underlyingDictionary); + + OnDeserializing(reader, contract, underlyingDictionary); + + int initialDepth = reader.Depth; + + if (contract.KeyContract == null) + contract.KeyContract = GetContractSafe(contract.DictionaryKeyType); + + if (contract.ItemContract == null) + contract.ItemContract = GetContractSafe(contract.DictionaryValueType); + + JsonConverter dictionaryValueConverter = contract.ItemConverter ?? GetConverter(contract.ItemContract, null, contract, containerProperty); + PrimitiveTypeCode keyTypeCode = (contract.KeyContract is JsonPrimitiveContract) ? ((JsonPrimitiveContract)contract.KeyContract).TypeCode : PrimitiveTypeCode.Empty; + + bool finished = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + object keyValue = reader.Value; + if (CheckPropertyName(reader, keyValue.ToString())) + continue; + + try + { + try + { + DateParseHandling dateParseHandling; + switch (keyTypeCode) + { + case PrimitiveTypeCode.DateTime: + case PrimitiveTypeCode.DateTimeNullable: + dateParseHandling = DateParseHandling.DateTime; + break; +#if !NET20 + case PrimitiveTypeCode.DateTimeOffset: + case PrimitiveTypeCode.DateTimeOffsetNullable: + dateParseHandling = DateParseHandling.DateTimeOffset; + break; +#endif + default: + dateParseHandling = DateParseHandling.None; + break; + } + + // this is for correctly reading ISO and MS formatted dictionary keys + object dt; + if (dateParseHandling != DateParseHandling.None && DateTimeUtils.TryParseDateTime(keyValue.ToString(), dateParseHandling, reader.DateTimeZoneHandling, reader.DateFormatString, reader.Culture, out dt)) + keyValue = dt; + else + keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Could not convert string '{0}' to dictionary key type '{1}'. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex); + } + + if (!ReadForType(reader, contract.ItemContract, dictionaryValueConverter != null)) + throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object."); + + object itemValue; + if (dictionaryValueConverter != null && dictionaryValueConverter.CanRead) + itemValue = DeserializeConvertable(dictionaryValueConverter, reader, contract.DictionaryValueType, null); + else + itemValue = CreateValueInternal(reader, contract.DictionaryValueType, contract.ItemContract, null, contract, containerProperty, null); + + dictionary[keyValue] = itemValue; + } + catch (Exception ex) + { + if (IsErrorHandled(underlyingDictionary, contract, keyValue, reader as IJsonLineInfo, reader.Path, ex)) + HandleError(reader, true, initialDepth); + else + throw; + } + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + finished = true; + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, underlyingDictionary, "Unexpected end when deserializing object."); + + OnDeserialized(reader, contract, underlyingDictionary); + return underlyingDictionary; + } + + private object PopulateMultidimensionalArray(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) + { + int rank = contract.UnderlyingType.GetArrayRank(); + + if (id != null) + AddReference(reader, id, list); + + OnDeserializing(reader, contract, list); + + JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType); + JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null, contract, containerProperty); + + int? previousErrorIndex = null; + Stack listStack = new Stack(); + listStack.Push(list); + IList currentList = list; + + bool finished = false; + do + { + int initialDepth = reader.Depth; + + if (listStack.Count == rank) + { + try + { + if (ReadForType(reader, collectionItemContract, collectionItemConverter != null)) + { + switch (reader.TokenType) + { + case JsonToken.EndArray: + listStack.Pop(); + currentList = listStack.Peek(); + previousErrorIndex = null; + break; + case JsonToken.Comment: + break; + default: + object value; + + if (collectionItemConverter != null && collectionItemConverter.CanRead) + value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null); + else + value = CreateValueInternal(reader, contract.CollectionItemType, collectionItemContract, null, contract, containerProperty, null); + + currentList.Add(value); + break; + } + } + else + { + break; + } + } + catch (Exception ex) + { + JsonPosition errorPosition = reader.GetPosition(initialDepth); + + if (IsErrorHandled(list, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) + { + HandleError(reader, true, initialDepth); + + if (previousErrorIndex != null && previousErrorIndex == errorPosition.Position) + { + // reader index has not moved since previous error handling + // break out of reading array to prevent infinite loop + throw JsonSerializationException.Create(reader, "Infinite loop detected from error handling.", ex); + } + else + { + previousErrorIndex = errorPosition.Position; + } + } + else + { + throw; + } + } + } + else + { + if (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.StartArray: + IList newList = new List(); + currentList.Add(newList); + listStack.Push(newList); + currentList = newList; + break; + case JsonToken.EndArray: + listStack.Pop(); + + if (listStack.Count > 0) + { + currentList = listStack.Peek(); + } + else + { + finished = true; + } + break; + case JsonToken.Comment: + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing multidimensional array: " + reader.TokenType); + } + } + else + { + break; + } + } + } while (!finished); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); + + OnDeserialized(reader, contract, list); + return list; + } + + private void ThrowUnexpectedEndException(JsonReader reader, JsonContract contract, object currentObject, string message) + { + try + { + throw JsonSerializationException.Create(reader, message); + } + catch (Exception ex) + { + if (IsErrorHandled(currentObject, contract, null, reader as IJsonLineInfo, reader.Path, ex)) + HandleError(reader, false, 0); + else + throw; + } + } + + private object PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, string id) + { + IWrappedCollection wrappedCollection = list as IWrappedCollection; + object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : list; + + if (id != null) + AddReference(reader, id, underlyingList); + + // can't populate an existing array + if (list.IsFixedSize) + { + reader.Skip(); + return underlyingList; + } + + OnDeserializing(reader, contract, underlyingList); + + int initialDepth = reader.Depth; + + if (contract.ItemContract == null) + contract.ItemContract = GetContractSafe(contract.CollectionItemType); + + JsonConverter collectionItemConverter = GetConverter(contract.ItemContract, null, contract, containerProperty); + + int? previousErrorIndex = null; + + bool finished = false; + do + { + try + { + if (ReadForType(reader, contract.ItemContract, collectionItemConverter != null)) + { + switch (reader.TokenType) + { + case JsonToken.EndArray: + finished = true; + break; + case JsonToken.Comment: + break; + default: + object value; + + if (collectionItemConverter != null && collectionItemConverter.CanRead) + value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null); + else + value = CreateValueInternal(reader, contract.CollectionItemType, contract.ItemContract, null, contract, containerProperty, null); + + list.Add(value); + break; + } + } + else + { + break; + } + } + catch (Exception ex) + { + JsonPosition errorPosition = reader.GetPosition(initialDepth); + + if (IsErrorHandled(underlyingList, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) + { + HandleError(reader, true, initialDepth); + + if (previousErrorIndex != null && previousErrorIndex == errorPosition.Position) + { + // reader index has not moved since previous error handling + // break out of reading array to prevent infinite loop + throw JsonSerializationException.Create(reader, "Infinite loop detected from error handling.", ex); + } + else + { + previousErrorIndex = errorPosition.Position; + } + } + else + { + throw; + } + } + } while (!finished); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, underlyingList, "Unexpected end when deserializing array."); + + OnDeserialized(reader, contract, underlyingList); + return underlyingList; + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + private object CreateISerializable(JsonReader reader, JsonISerializableContract contract, JsonProperty member, string id) + { + Type objectType = contract.UnderlyingType; + + if (!JsonTypeReflector.FullyTrusted) + { + string message = @"Type '{0}' implements ISerializable but cannot be deserialized using the ISerializable interface because the current application is not fully trusted and ISerializable can expose secure data." + Environment.NewLine + + @"To fix this error either change the environment to be fully trusted, change the application to not deserialize the type, add JsonObjectAttribute to the type or change the JsonSerializer setting ContractResolver to use a new DefaultContractResolver with IgnoreSerializableInterface set to true." + Environment.NewLine; + message = message.FormatWith(CultureInfo.InvariantCulture, objectType); + + throw JsonSerializationException.Create(reader, message); + } + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using ISerializable constructor.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new JsonFormatterConverter(this, contract, member)); + + bool finished = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + serializationInfo.AddValue(memberName, JToken.ReadFrom(reader)); + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + finished = true; + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, serializationInfo, "Unexpected end when deserializing object."); + + if (contract.ISerializableCreator == null) + throw JsonSerializationException.Create(reader, "ISerializable type '{0}' does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + object createdObject = contract.ISerializableCreator(serializationInfo, Serializer._context); + + if (id != null) + AddReference(reader, id, createdObject); + + // these are together because OnDeserializing takes an object but for an ISerializable the object is fully created in the constructor + OnDeserializing(reader, contract, createdObject); + OnDeserialized(reader, contract, createdObject); + + return createdObject; + } + + internal object CreateISerializableItem(JToken token, Type type, JsonISerializableContract contract, JsonProperty member) + { + JsonContract itemContract = GetContractSafe(type); + JsonConverter itemConverter = GetConverter(itemContract, null, contract, member); + + JsonReader tokenReader = token.CreateReader(); + CheckedRead(tokenReader); // Move to first token + + object result; + if (itemConverter != null && itemConverter.CanRead) + result = DeserializeConvertable(itemConverter, tokenReader, type, null); + else + result = CreateValueInternal(tokenReader, type, itemContract, null, contract, member, null); + + return result; + } +#endif + +#if !(NET35 || NET20 || PORTABLE40) + private object CreateDynamic(JsonReader reader, JsonDynamicContract contract, JsonProperty member, string id) + { + IDynamicMetaObjectProvider newObject; + + if (!contract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.DefaultCreator != null && + (!contract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + newObject = (IDynamicMetaObjectProvider)contract.DefaultCreator(); + else + throw JsonSerializationException.Create(reader, "Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (id != null) + AddReference(reader, id, newObject); + + OnDeserializing(reader, contract, newObject); + + int initialDepth = reader.Depth; + + bool finished = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + + try + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + // first attempt to find a settable property, otherwise fall back to a dynamic set without type + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property != null && property.Writable && !property.Ignored) + { + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter, null, null); + + if (!SetPropertyValue(property, propertyConverter, null, member, reader, newObject)) + reader.Skip(); + } + else + { + Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof(IDynamicMetaObjectProvider); + + JsonContract dynamicMemberContract = GetContractSafe(t); + JsonConverter dynamicMemberConverter = GetConverter(dynamicMemberContract, null, null, member); + + object value; + if (dynamicMemberConverter != null && dynamicMemberConverter.CanRead) + value = DeserializeConvertable(dynamicMemberConverter, reader, t, null); + else + value = CreateValueInternal(reader, t, dynamicMemberContract, null, null, member, null); + + contract.TrySetMember(newObject, memberName, value); + } + } + catch (Exception ex) + { + if (IsErrorHandled(newObject, contract, memberName, reader as IJsonLineInfo, reader.Path, ex)) + HandleError(reader, true, initialDepth); + else + throw; + } + break; + case JsonToken.EndObject: + finished = true; + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object."); + + OnDeserialized(reader, contract, newObject); + + return newObject; + } +#endif + + private object CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor creator, string id) + { + ValidationUtils.ArgumentNotNull(creator, "creator"); + + // only need to keep a track of properies presence if they are required or a value should be defaulted if missing + Dictionary propertiesPresence = (contract.HasRequiredOrDefaultValueProperties || HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Populate)) + ? contract.Properties.ToDictionary(m => m, m => PropertyPresence.None) + : null; + + Type objectType = contract.UnderlyingType; + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + { + string parameters = string.Join(", ", contract.CreatorParameters.Select(p => p.PropertyName).ToArray()); + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using creator with parameters: {1}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType, parameters)), null); + } + + IDictionary extensionData; + IDictionary propertyValues = ResolvePropertyAndCreatorValues(contract, containerProperty, reader, objectType, out extensionData); + + object[] creatorParameterValues = new object[contract.CreatorParameters.Count]; + IDictionary remainingPropertyValues = new Dictionary(); + + foreach (KeyValuePair propertyValue in propertyValues) + { + JsonProperty property = propertyValue.Key; + + JsonProperty matchingCreatorParameter; + if (contract.CreatorParameters.Contains(property)) + { + matchingCreatorParameter = property; + } + else + { + // check to see if a parameter with the same name as the underlying property name exists and match to that + matchingCreatorParameter = contract.CreatorParameters.ForgivingCaseSensitiveFind(p => p.PropertyName, property.UnderlyingName); + } + + if (matchingCreatorParameter != null) + { + int i = contract.CreatorParameters.IndexOf(matchingCreatorParameter); + creatorParameterValues[i] = propertyValue.Value; + } + else + { + remainingPropertyValues.Add(propertyValue); + } + + if (propertiesPresence != null) + { + // map from creator property to normal property + JsonProperty presenceProperty = propertiesPresence.Keys.FirstOrDefault(p => p.PropertyName == property.PropertyName); + if (presenceProperty != null) + propertiesPresence[presenceProperty] = (propertyValue.Value == null) ? PropertyPresence.Null : PropertyPresence.Value; + } + } + + object createdObject = creator(creatorParameterValues); + + if (id != null) + AddReference(reader, id, createdObject); + + OnDeserializing(reader, contract, createdObject); + + // go through unused values and set the newly created object's properties + foreach (KeyValuePair remainingPropertyValue in remainingPropertyValues) + { + JsonProperty property = remainingPropertyValue.Key; + object value = remainingPropertyValue.Value; + + if (ShouldSetPropertyValue(property, value)) + { + property.ValueProvider.SetValue(createdObject, value); + } + else if (!property.Writable && value != null) + { + // handle readonly collection/dictionary properties + JsonContract propertyContract = Serializer._contractResolver.ResolveContract(property.PropertyType); + + if (propertyContract.ContractType == JsonContractType.Array) + { + JsonArrayContract propertyArrayContract = (JsonArrayContract)propertyContract; + + object createdObjectCollection = property.ValueProvider.GetValue(createdObject); + if (createdObjectCollection != null) + { + IWrappedCollection createdObjectCollectionWrapper = propertyArrayContract.CreateWrapper(createdObjectCollection); + IWrappedCollection newValues = propertyArrayContract.CreateWrapper(value); + + foreach (object newValue in newValues) + { + createdObjectCollectionWrapper.Add(newValue); + } + } + } + else if (propertyContract.ContractType == JsonContractType.Dictionary) + { + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)propertyContract; + + object createdObjectDictionary = property.ValueProvider.GetValue(createdObject); + if (createdObjectDictionary != null) + { + IDictionary targetDictionary = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(createdObjectDictionary) : (IDictionary)createdObjectDictionary; + IDictionary newValues = (dictionaryContract.ShouldCreateWrapper) ? dictionaryContract.CreateWrapper(value) : (IDictionary)value; + + foreach (DictionaryEntry newValue in newValues) + { + targetDictionary.Add(newValue.Key, newValue.Value); + } + } + } + } + } + + if (extensionData != null) + { + foreach (KeyValuePair e in extensionData) + { + contract.ExtensionDataSetter(createdObject, e.Key, e.Value); + } + } + + EndObject(createdObject, reader, contract, reader.Depth, propertiesPresence); + + OnDeserialized(reader, contract, createdObject); + return createdObject; + } + + private object DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, object existingValue) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Started deserializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, objectType, converter.GetType())), null); + + object value = converter.ReadJson(reader, objectType, existingValue, GetInternalSerializer()); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Finished deserializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, objectType, converter.GetType())), null); + + return value; + } + + private IDictionary ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType, out IDictionary extensionData) + { + extensionData = (contract.ExtensionDataSetter != null) ? new Dictionary() : null; + + IDictionary propertyValues = new Dictionary(); + bool exit = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + + // attempt exact case match first + // then try match ignoring case + JsonProperty property = contract.CreatorParameters.GetClosestMatchProperty(memberName) ?? + contract.Properties.GetClosestMatchProperty(memberName); + + if (property != null) + { + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter, contract, containerProperty); + + if (!ReadForType(reader, property.PropertyContract, propertyConverter != null)) + throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + if (!property.Ignored) + { + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + object propertyValue; + if (propertyConverter != null && propertyConverter.CanRead) + propertyValue = DeserializeConvertable(propertyConverter, reader, property.PropertyType, null); + else + propertyValue = CreateValueInternal(reader, property.PropertyType, property.PropertyContract, property, contract, containerProperty, null); + + propertyValues[property] = propertyValue; + continue; + } + } + else + { + if (!reader.Read()) + throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}.".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType)), null); + + if (Serializer._missingMemberHandling == MissingMemberHandling.Error) + throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); + } + + if (extensionData != null) + { + object value = CreateValueInternal(reader, null, null, null, contract, containerProperty, null); + extensionData[memberName] = value; + } + else + { + reader.Skip(); + } + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + return propertyValues; + } + + private bool ReadForType(JsonReader reader, JsonContract contract, bool hasConverter) + { + // don't read properties with converters as a specific value + // the value might be a string which will then get converted which will error if read as date for example + if (hasConverter) + return reader.Read(); + + ReadType t = (contract != null) ? contract.InternalReadType : ReadType.Read; + + switch (t) + { + case ReadType.Read: + do + { + if (!reader.Read()) + return false; + } while (reader.TokenType == JsonToken.Comment); + + return true; + case ReadType.ReadAsInt32: + reader.ReadAsInt32(); + break; + case ReadType.ReadAsDecimal: + reader.ReadAsDecimal(); + break; + case ReadType.ReadAsBytes: + reader.ReadAsBytes(); + break; + case ReadType.ReadAsString: + reader.ReadAsString(); + break; + case ReadType.ReadAsDateTime: + reader.ReadAsDateTime(); + break; +#if !NET20 + case ReadType.ReadAsDateTimeOffset: + reader.ReadAsDateTimeOffset(); + break; +#endif + default: + throw new ArgumentOutOfRangeException(); + } + + return (reader.TokenType != JsonToken.None); + } + + public object CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, string id, out bool createdFromNonDefaultCreator) + { + object newObject = null; + + if (objectContract.OverrideCreator != null) + { + if (objectContract.CreatorParameters.Count > 0) + { + createdFromNonDefaultCreator = true; + return CreateObjectUsingCreatorWithParameters(reader, objectContract, containerMember, objectContract.OverrideCreator, id); + } + + newObject = objectContract.OverrideCreator(new object[0]); + } + else if (objectContract.DefaultCreator != null && + (!objectContract.DefaultCreatorNonPublic || Serializer._constructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor || objectContract.ParametrizedCreator == null)) + { + // use the default constructor if it is... + // public + // non-public and the user has change constructor handling settings + // non-public and there is no other creator + newObject = objectContract.DefaultCreator(); + } + else if (objectContract.ParametrizedCreator != null) + { + createdFromNonDefaultCreator = true; + return CreateObjectUsingCreatorWithParameters(reader, objectContract, containerMember, objectContract.ParametrizedCreator, id); + } + + if (newObject == null) + { + if (!objectContract.IsInstantiable) + throw JsonSerializationException.Create(reader, "Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType)); + + throw JsonSerializationException.Create(reader, "Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, objectContract.UnderlyingType)); + } + + createdFromNonDefaultCreator = false; + return newObject; + } + + private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, string id) + { + OnDeserializing(reader, contract, newObject); + + // only need to keep a track of properies presence if they are required or a value should be defaulted if missing + Dictionary propertiesPresence = (contract.HasRequiredOrDefaultValueProperties || HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Populate)) + ? contract.Properties.ToDictionary(m => m, m => PropertyPresence.None) + : null; + + if (id != null) + AddReference(reader, id, newObject); + + int initialDepth = reader.Depth; + + bool finished = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + { + string memberName = reader.Value.ToString(); + + if (CheckPropertyName(reader, memberName)) + continue; + + try + { + // attempt exact case match first + // then try match ignoring case + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property == null) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType)), null); + + if (Serializer._missingMemberHandling == MissingMemberHandling.Error) + throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); + + if (!reader.Read()) + break; + + SetExtensionData(contract, member, reader, memberName, newObject); + continue; + } + + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter, contract, member); + + if (!ReadForType(reader, property.PropertyContract, propertyConverter != null)) + throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + SetPropertyPresence(reader, property, propertiesPresence); + + // set extension data if property is ignored or readonly + if (!SetPropertyValue(property, propertyConverter, contract, member, reader, newObject)) + SetExtensionData(contract, member, reader, memberName, newObject); + } + catch (Exception ex) + { + if (IsErrorHandled(newObject, contract, memberName, reader as IJsonLineInfo, reader.Path, ex)) + HandleError(reader, true, initialDepth); + else + throw; + } + break; + } + case JsonToken.EndObject: + finished = true; + break; + case JsonToken.Comment: + // ignore + break; + default: + throw JsonSerializationException.Create(reader, "Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!finished && reader.Read()); + + if (!finished) + ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object."); + + EndObject(newObject, reader, contract, initialDepth, propertiesPresence); + + OnDeserialized(reader, contract, newObject); + return newObject; + } + + private bool CheckPropertyName(JsonReader reader, string memberName) + { + if (Serializer.MetadataPropertyHandling == MetadataPropertyHandling.ReadAhead) + { + switch (memberName) + { + case JsonTypeReflector.IdPropertyName: + case JsonTypeReflector.RefPropertyName: + case JsonTypeReflector.TypePropertyName: + case JsonTypeReflector.ArrayValuesPropertyName: + reader.Skip(); + return true; + } + } + return false; + } + + private void SetExtensionData(JsonObjectContract contract, JsonProperty member, JsonReader reader, string memberName, object o) + { + if (contract.ExtensionDataSetter != null) + { + try + { + object value = CreateValueInternal(reader, null, null, null, contract, member, null); + + contract.ExtensionDataSetter(o, memberName, value); + } + catch (Exception ex) + { + throw JsonSerializationException.Create(reader, "Error setting value in extension data for type '{0}'.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType), ex); + } + } + else + { + reader.Skip(); + } + } + + private void EndObject(object newObject, JsonReader reader, JsonObjectContract contract, int initialDepth, Dictionary propertiesPresence) + { + if (propertiesPresence != null) + { + foreach (KeyValuePair propertyPresence in propertiesPresence) + { + JsonProperty property = propertyPresence.Key; + PropertyPresence presence = propertyPresence.Value; + + if (presence == PropertyPresence.None || presence == PropertyPresence.Null) + { + try + { + Required resolvedRequired = property._required ?? contract.ItemRequired ?? Required.Default; + + switch (presence) + { + case PropertyPresence.None: + if (resolvedRequired == Required.AllowNull || resolvedRequired == Required.Always) + throw JsonSerializationException.Create(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); + + if (property.PropertyContract == null) + property.PropertyContract = GetContractSafe(property.PropertyType); + + if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Populate) && property.Writable && !property.Ignored) + property.ValueProvider.SetValue(newObject, EnsureType(reader, property.GetResolvedDefaultValue(), CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType)); + break; + case PropertyPresence.Null: + if (resolvedRequired == Required.Always) + throw JsonSerializationException.Create(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); + break; + } + } + catch (Exception ex) + { + if (IsErrorHandled(newObject, contract, property.PropertyName, reader as IJsonLineInfo, reader.Path, ex)) + HandleError(reader, true, initialDepth); + else + throw; + } + } + } + } + } + + private void SetPropertyPresence(JsonReader reader, JsonProperty property, Dictionary requiredProperties) + { + if (property != null && requiredProperties != null) + { + requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined) + ? PropertyPresence.Null + : PropertyPresence.Value; + } + } + + private void HandleError(JsonReader reader, bool readPastError, int initialDepth) + { + ClearErrorContext(); + + if (readPastError) + { + reader.Skip(); + + while (reader.Depth > (initialDepth + 1)) + { + if (!reader.Read()) + break; + } + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonSerializerInternalWriter.cs b/Libs/JsonNet/Serialization/JsonSerializerInternalWriter.cs new file mode 100644 index 0000000..7c0013d --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonSerializerInternalWriter.cs @@ -0,0 +1,1040 @@ +#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; +using System.Collections.Generic; +using System.ComponentModel; +#if !(NET35 || NET20 || PORTABLE40) +using System.Dynamic; +#endif +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Security; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerInternalWriter : JsonSerializerInternalBase + { + private JsonContract _rootContract; + private int _rootLevel; + private readonly List _serializeStack = new List(); + private JsonSerializerProxy _internalSerializer; + + public JsonSerializerInternalWriter(JsonSerializer serializer) + : base(serializer) + { + } + + public void Serialize(JsonWriter jsonWriter, object value, Type objectType) + { + if (jsonWriter == null) + throw new ArgumentNullException("jsonWriter"); + + _rootContract = (objectType != null) ? Serializer._contractResolver.ResolveContract(objectType) : null; + _rootLevel = _serializeStack.Count + 1; + + JsonContract contract = GetContractSafe(value); + + try + { + SerializeValue(jsonWriter, value, contract, null, null, null); + } + catch (Exception ex) + { + if (IsErrorHandled(null, contract, null, null, jsonWriter.Path, ex)) + { + HandleError(jsonWriter, 0); + } + else + { + // clear context in case serializer is being used inside a converter + // if the converter wraps the error then not clearing the context will cause this error: + // "Current error context error is different to requested error." + ClearErrorContext(); + throw; + } + } + finally + { + // clear root contract to ensure that if level was > 1 then it won't + // accidently be used for non root values + _rootContract = null; + } + } + + private JsonSerializerProxy GetInternalSerializer() + { + if (_internalSerializer == null) + _internalSerializer = new JsonSerializerProxy(this); + + return _internalSerializer; + } + + private JsonContract GetContractSafe(object value) + { + if (value == null) + return null; + + return Serializer._contractResolver.ResolveContract(value.GetType()); + } + + private void SerializePrimitive(JsonWriter writer, object value, JsonPrimitiveContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) + { + if (contract.TypeCode == PrimitiveTypeCode.Bytes) + { + // if type name handling is enabled then wrap the base64 byte string in an object with the type name + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Objects, contract, member, containerContract, containerProperty); + if (includeTypeDetails) + { + writer.WriteStartObject(); + WriteTypeProperty(writer, contract.CreatedType); + writer.WritePropertyName(JsonTypeReflector.ValuePropertyName, false); + + JsonWriter.WriteValue(writer, contract.TypeCode, value); + + writer.WriteEndObject(); + return; + } + } + + JsonWriter.WriteValue(writer, contract.TypeCode, value); + } + + private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + JsonConverter converter = + ((member != null) ? member.Converter : null) ?? + ((containerProperty != null) ? containerProperty.ItemConverter : null) ?? + ((containerContract != null) ? containerContract.ItemConverter : null) ?? + valueContract.Converter ?? + Serializer.GetMatchingConverter(valueContract.UnderlyingType) ?? + valueContract.InternalConverter; + + if (converter != null && converter.CanWrite) + { + SerializeConvertable(writer, converter, value, valueContract, containerContract, containerProperty); + return; + } + + switch (valueContract.ContractType) + { + case JsonContractType.Object: + SerializeObject(writer, value, (JsonObjectContract)valueContract, member, containerContract, containerProperty); + break; + case JsonContractType.Array: + JsonArrayContract arrayContract = (JsonArrayContract)valueContract; + if (!arrayContract.IsMultidimensionalArray) + SerializeList(writer, (IEnumerable)value, arrayContract, member, containerContract, containerProperty); + else + SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty); + break; + case JsonContractType.Primitive: + SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, containerContract, containerProperty); + break; + case JsonContractType.String: + SerializeString(writer, value, (JsonStringContract)valueContract); + break; + case JsonContractType.Dictionary: + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract; + SerializeDictionary(writer, (value is IDictionary) ? (IDictionary)value : dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty); + break; +#if !(NET35 || NET20 || PORTABLE40) + case JsonContractType.Dynamic: + SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, containerContract, containerProperty); + break; +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + case JsonContractType.Serializable: + SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, containerContract, containerProperty); + break; +#endif + case JsonContractType.Linq: + ((JToken)value).WriteTo(writer, Serializer.Converters.ToArray()); + break; + } + } + + private bool? ResolveIsReference(JsonContract contract, JsonProperty property, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + bool? isReference = null; + + // value could be coming from a dictionary or array and not have a property + if (property != null) + isReference = property.IsReference; + + if (isReference == null && containerProperty != null) + isReference = containerProperty.ItemIsReference; + + if (isReference == null && collectionContract != null) + isReference = collectionContract.ItemIsReference; + + if (isReference == null) + isReference = contract.IsReference; + + return isReference; + } + + private bool ShouldWriteReference(object value, JsonProperty property, JsonContract valueContract, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + if (value == null) + return false; + if (valueContract.ContractType == JsonContractType.Primitive || valueContract.ContractType == JsonContractType.String) + return false; + + bool? isReference = ResolveIsReference(valueContract, property, collectionContract, containerProperty); + + if (isReference == null) + { + if (valueContract.ContractType == JsonContractType.Array) + isReference = HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Arrays); + else + isReference = HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Objects); + } + + if (!isReference.Value) + return false; + + return Serializer.GetReferenceResolver().IsReferenced(this, value); + } + + private bool ShouldWriteProperty(object memberValue, JsonProperty property) + { + if (property.NullValueHandling.GetValueOrDefault(Serializer._nullValueHandling) == NullValueHandling.Ignore && + memberValue == null) + return false; + + if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer._defaultValueHandling), DefaultValueHandling.Ignore) + && MiscellaneousUtils.ValueEquals(memberValue, property.GetResolvedDefaultValue())) + return false; + + return true; + } + + private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty) + { + if (value == null || contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String) + return true; + + ReferenceLoopHandling? referenceLoopHandling = null; + + if (property != null) + referenceLoopHandling = property.ReferenceLoopHandling; + + if (referenceLoopHandling == null && containerProperty != null) + referenceLoopHandling = containerProperty.ItemReferenceLoopHandling; + + if (referenceLoopHandling == null && containerContract != null) + referenceLoopHandling = containerContract.ItemReferenceLoopHandling; + + if (_serializeStack.IndexOf(value) != -1) + { + string message = "Self referencing loop detected"; + if (property != null) + message += " for property '{0}'".FormatWith(CultureInfo.InvariantCulture, property.PropertyName); + message += " with type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()); + + switch (referenceLoopHandling.GetValueOrDefault(Serializer._referenceLoopHandling)) + { + case ReferenceLoopHandling.Error: + throw JsonSerializationException.Create(null, writer.ContainerPath, message, null); + case ReferenceLoopHandling.Ignore: + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Skipping serializing self referenced value."), null); + + return false; + case ReferenceLoopHandling.Serialize: + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Serializing self referenced value."), null); + + return true; + } + } + + return true; + } + + private void WriteReference(JsonWriter writer, object value) + { + string reference = GetReference(writer, value); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference to Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, value.GetType())), null); + + writer.WriteStartObject(); + writer.WritePropertyName(JsonTypeReflector.RefPropertyName, false); + writer.WriteValue(reference); + writer.WriteEndObject(); + } + + private string GetReference(JsonWriter writer, object value) + { + try + { + string reference = Serializer.GetReferenceResolver().GetReference(this, value); + + return reference; + } + catch (Exception ex) + { + throw JsonSerializationException.Create(null, writer.ContainerPath, "Error writing object reference for '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()), ex); + } + } + + internal static bool TryConvertToString(object value, Type type, out string s) + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + TypeConverter converter = ConvertUtils.GetConverter(type); + + // use the objectType's TypeConverter if it has one and can convert to a string + if (converter != null + && !(converter is ComponentConverter) + && converter.GetType() != typeof(TypeConverter)) + { + if (converter.CanConvertTo(typeof(string))) + { + s = converter.ConvertToInvariantString(value); + return true; + } + } +#endif + +#if NETFX_CORE || PORTABLE + if (value is Guid || value is Uri || value is TimeSpan) + { + s = value.ToString(); + return true; + } +#endif + + if (value is Type) + { + s = ((Type)value).AssemblyQualifiedName; + return true; + } + + s = null; + return false; + } + + private void SerializeString(JsonWriter writer, object value, JsonStringContract contract) + { + OnSerializing(writer, contract, value); + + string s; + TryConvertToString(value, contract.UnderlyingType, out s); + writer.WriteValue(s); + + OnSerialized(writer, contract, value); + } + + private void OnSerializing(JsonWriter writer, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnSerializing(value, Serializer._context); + } + + private void OnSerialized(JsonWriter writer, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnSerialized(value, Serializer._context); + } + + private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + OnSerializing(writer, contract, value); + + _serializeStack.Add(value); + + WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty); + + int initialDepth = writer.Top; + + for (int index = 0; index < contract.Properties.Count; index++) + { + JsonProperty property = contract.Properties[index]; + try + { + object memberValue; + JsonContract memberContract; + + if (!CalculatePropertyValues(writer, value, contract, member, property, out memberContract, out memberValue)) + continue; + + property.WritePropertyName(writer); + SerializeValue(writer, memberValue, memberContract, property, contract, member); + } + catch (Exception ex) + { + if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + + if (contract.ExtensionDataGetter != null) + { + IEnumerable> extensionData = contract.ExtensionDataGetter(value); + if (extensionData != null) + { + foreach (KeyValuePair e in extensionData) + { + JsonContract keyContract = GetContractSafe(e.Key); + JsonContract valueContract = GetContractSafe(e.Value); + + bool escape; + string propertyName = GetPropertyName(writer, e.Key, keyContract, out escape); + + if (ShouldWriteReference(e.Value, null, valueContract, contract, member)) + { + writer.WritePropertyName(propertyName); + WriteReference(writer, e.Value); + } + else + { + if (!CheckForCircularReference(writer, e.Value, null, valueContract, contract, member)) + continue; + + writer.WritePropertyName(propertyName); + + SerializeValue(writer, e.Value, valueContract, null, contract, member); + } + } + } + } + + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + + OnSerialized(writer, contract, value); + } + + private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue) + { + if (!property.Ignored && property.Readable && ShouldSerialize(writer, property, value) && IsSpecified(writer, property, value)) + { + if (property.PropertyContract == null) + property.PropertyContract = Serializer._contractResolver.ResolveContract(property.PropertyType); + + memberValue = property.ValueProvider.GetValue(value); + memberContract = (property.PropertyContract.IsSealed) ? property.PropertyContract : GetContractSafe(memberValue); + + if (ShouldWriteProperty(memberValue, property)) + { + if (ShouldWriteReference(memberValue, property, memberContract, contract, member)) + { + property.WritePropertyName(writer); + WriteReference(writer, memberValue); + return false; + } + + if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member)) + return false; + + if (memberValue == null) + { + JsonObjectContract objectContract = contract as JsonObjectContract; + Required resolvedRequired = property._required ?? ((objectContract != null) ? objectContract.ItemRequired : null) ?? Required.Default; + if (resolvedRequired == Required.Always) + throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null); + } + + return true; + } + } + + memberContract = null; + memberValue = null; + return false; + } + + private void WriteObjectStart(JsonWriter writer, object value, JsonContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + writer.WriteStartObject(); + + bool isReference = ResolveIsReference(contract, member, collectionContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Objects); + if (isReference) + { + WriteReferenceIdProperty(writer, contract.UnderlyingType, value); + } + if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, containerProperty)) + { + WriteTypeProperty(writer, contract.UnderlyingType); + } + } + + private void WriteReferenceIdProperty(JsonWriter writer, Type type, object value) + { + string reference = GetReference(writer, value); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, type)), null); + + writer.WritePropertyName(JsonTypeReflector.IdPropertyName, false); + writer.WriteValue(reference); + } + + private void WriteTypeProperty(JsonWriter writer, Type type) + { + string typeName = ReflectionUtils.GetTypeName(type, Serializer._typeNameAssemblyFormat, Serializer._binder); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing type name '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, typeName, type)), null); + + writer.WritePropertyName(JsonTypeReflector.TypePropertyName, false); + writer.WriteValue(typeName); + } + + private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag) + { + return ((value & flag) == flag); + } + + private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag) + { + return ((value & flag) == flag); + } + + private bool HasFlag(TypeNameHandling value, TypeNameHandling flag) + { + return ((value & flag) == flag); + } + + private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + if (ShouldWriteReference(value, null, contract, collectionContract, containerProperty)) + { + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(writer, value, null, contract, collectionContract, containerProperty)) + return; + + _serializeStack.Add(value); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null); + + converter.WriteJson(writer, value, GetInternalSerializer()); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + } + } + + private void SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + IWrappedCollection wrappedCollection = values as IWrappedCollection; + object underlyingList = wrappedCollection != null ? wrappedCollection.UnderlyingCollection : values; + + OnSerializing(writer, contract, underlyingList); + + _serializeStack.Add(underlyingList); + + bool hasWrittenMetadataObject = WriteStartArray(writer, underlyingList, contract, member, collectionContract, containerProperty); + + writer.WriteStartArray(); + + int initialDepth = writer.Top; + + int index = 0; + // note that an error in the IEnumerable won't be caught + foreach (object value in values) + { + try + { + JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value); + + if (ShouldWriteReference(value, null, valueContract, contract, member)) + { + WriteReference(writer, value); + } + else + { + if (CheckForCircularReference(writer, value, null, valueContract, contract, member)) + { + SerializeValue(writer, value, valueContract, null, contract, member); + } + } + } + catch (Exception ex) + { + if (IsErrorHandled(underlyingList, contract, index, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth); + else + throw; + } + finally + { + index++; + } + } + + writer.WriteEndArray(); + + if (hasWrittenMetadataObject) + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + + OnSerialized(writer, contract, underlyingList); + } + + private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + OnSerializing(writer, contract, values); + + _serializeStack.Add(values); + + bool hasWrittenMetadataObject = WriteStartArray(writer, values, contract, member, collectionContract, containerProperty); + + SerializeMultidimensionalArray(writer, values, contract, member, writer.Top, new int[0]); + + if (hasWrittenMetadataObject) + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + + OnSerialized(writer, contract, values); + } + + private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, int initialDepth, int[] indices) + { + int dimension = indices.Length; + int[] newIndices = new int[dimension + 1]; + for (int i = 0; i < dimension; i++) + { + newIndices[i] = indices[i]; + } + + writer.WriteStartArray(); + + for (int i = 0; i < values.GetLength(dimension); i++) + { + newIndices[dimension] = i; + bool isTopLevel = (newIndices.Length == values.Rank); + + if (isTopLevel) + { + object value = values.GetValue(newIndices); + + try + { + JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value); + + if (ShouldWriteReference(value, null, valueContract, contract, member)) + { + WriteReference(writer, value); + } + else + { + if (CheckForCircularReference(writer, value, null, valueContract, contract, member)) + { + SerializeValue(writer, value, valueContract, null, contract, member); + } + } + } + catch (Exception ex) + { + if (IsErrorHandled(values, contract, i, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth + 1); + else + throw; + } + } + else + { + SerializeMultidimensionalArray(writer, values, contract, member, initialDepth + 1, newIndices); + } + } + + writer.WriteEndArray(); + } + + private bool WriteStartArray(JsonWriter writer, object values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) + { + bool isReference = ResolveIsReference(contract, member, containerContract, containerProperty) ?? HasFlag(Serializer._preserveReferencesHandling, PreserveReferencesHandling.Arrays); + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, containerContract, containerProperty); + bool writeMetadataObject = isReference || includeTypeDetails; + + if (writeMetadataObject) + { + writer.WriteStartObject(); + + if (isReference) + { + WriteReferenceIdProperty(writer, contract.UnderlyingType, values); + } + if (includeTypeDetails) + { + WriteTypeProperty(writer, values.GetType()); + } + writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName, false); + } + + if (contract.ItemContract == null) + contract.ItemContract = Serializer._contractResolver.ResolveContract(contract.CollectionItemType ?? typeof(object)); + + return writeMetadataObject; + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) +#if !(NET20 || NET35) + [SecuritySafeCritical] +#endif + private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + if (!JsonTypeReflector.FullyTrusted) + { + string message = @"Type '{0}' implements ISerializable but cannot be serialized using the ISerializable interface because the current application is not fully trusted and ISerializable can expose secure data." + Environment.NewLine + + @"To fix this error either change the environment to be fully trusted, change the application to not deserialize the type, add JsonObjectAttribute to the type or change the JsonSerializer setting ContractResolver to use a new DefaultContractResolver with IgnoreSerializableInterface set to true." + Environment.NewLine; + message = message.FormatWith(CultureInfo.InvariantCulture, value.GetType()); + + throw JsonSerializationException.Create(null, writer.ContainerPath, message, null); + } + + OnSerializing(writer, contract, value); + _serializeStack.Add(value); + + WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty); + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter()); + value.GetObjectData(serializationInfo, Serializer._context); + + foreach (SerializationEntry serializationEntry in serializationInfo) + { + JsonContract valueContract = GetContractSafe(serializationEntry.Value); + + if (ShouldWriteReference(serializationEntry.Value, null, valueContract, contract, member)) + { + writer.WritePropertyName(serializationEntry.Name); + WriteReference(writer, serializationEntry.Value); + } + else if (CheckForCircularReference(writer, serializationEntry.Value, null, valueContract, contract, member)) + { + writer.WritePropertyName(serializationEntry.Name); + SerializeValue(writer, serializationEntry.Value, valueContract, null, contract, member); + } + } + + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + OnSerialized(writer, contract, value); + } +#endif + +#if !(NET35 || NET20 || PORTABLE40) + private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + OnSerializing(writer, contract, value); + _serializeStack.Add(value); + + WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty); + + int initialDepth = writer.Top; + + for (int index = 0; index < contract.Properties.Count; index++) + { + JsonProperty property = contract.Properties[index]; + + // only write non-dynamic properties that have an explicit attribute + if (property.HasMemberAttribute) + { + try + { + object memberValue; + JsonContract memberContract; + + if (!CalculatePropertyValues(writer, value, contract, member, property, out memberContract, out memberValue)) + continue; + + property.WritePropertyName(writer); + SerializeValue(writer, memberValue, memberContract, property, contract, member); + } + catch (Exception ex) + { + if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + } + + foreach (string memberName in value.GetDynamicMemberNames()) + { + object memberValue; + if (contract.TryGetMember(value, memberName, out memberValue)) + { + try + { + JsonContract valueContract = GetContractSafe(memberValue); + + if (!ShouldWriteDynamicProperty(memberValue)) + continue; + + if (CheckForCircularReference(writer, memberValue, null, valueContract, contract, member)) + { + string resolvedPropertyName = (contract.PropertyNameResolver != null) + ? contract.PropertyNameResolver(memberName) + : memberName; + + writer.WritePropertyName(resolvedPropertyName); + SerializeValue(writer, memberValue, valueContract, null, contract, member); + } + } + catch (Exception ex) + { + if (IsErrorHandled(value, contract, memberName, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + } + + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + OnSerialized(writer, contract, value); + } +#endif + + private bool ShouldWriteDynamicProperty(object memberValue) + { + if (Serializer._nullValueHandling == NullValueHandling.Ignore && memberValue == null) + return false; + + if (HasFlag(Serializer._defaultValueHandling, DefaultValueHandling.Ignore) && + (memberValue == null || MiscellaneousUtils.ValueEquals(memberValue, ReflectionUtils.GetDefaultValue(memberValue.GetType())))) + return false; + + return true; + } + + private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) + { + TypeNameHandling resolvedTypeNameHandling = + ((member != null) ? member.TypeNameHandling : null) + ?? ((containerProperty != null) ? containerProperty.ItemTypeNameHandling : null) + ?? ((containerContract != null) ? containerContract.ItemTypeNameHandling : null) + ?? Serializer._typeNameHandling; + + if (HasFlag(resolvedTypeNameHandling, typeNameHandlingFlag)) + return true; + + // instance type and the property's type's contract default type are different (no need to put the type in JSON because the type will be created by default) + if (HasFlag(resolvedTypeNameHandling, TypeNameHandling.Auto)) + { + if (member != null) + { + if (contract.UnderlyingType != member.PropertyContract.CreatedType) + return true; + } + else if (containerContract != null) + { + if (containerContract.ItemContract == null || contract.UnderlyingType != containerContract.ItemContract.CreatedType) + return true; + } + else if (_rootContract != null && _serializeStack.Count == _rootLevel) + { + if (contract.UnderlyingType != _rootContract.CreatedType) + return true; + } + } + + return false; + } + + private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) + { + IWrappedDictionary wrappedDictionary = values as IWrappedDictionary; + object underlyingDictionary = wrappedDictionary != null ? wrappedDictionary.UnderlyingDictionary : values; + + OnSerializing(writer, contract, underlyingDictionary); + _serializeStack.Add(underlyingDictionary); + + WriteObjectStart(writer, underlyingDictionary, contract, member, collectionContract, containerProperty); + + if (contract.ItemContract == null) + contract.ItemContract = Serializer._contractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object)); + + if (contract.KeyContract == null) + contract.KeyContract = Serializer._contractResolver.ResolveContract(contract.DictionaryKeyType ?? typeof(object)); + + int initialDepth = writer.Top; + + foreach (DictionaryEntry entry in values) + { + bool escape; + string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out escape); + + propertyName = (contract.PropertyNameResolver != null) + ? contract.PropertyNameResolver(propertyName) + : propertyName; + + try + { + object value = entry.Value; + JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value); + + if (ShouldWriteReference(value, null, valueContract, contract, member)) + { + writer.WritePropertyName(propertyName, escape); + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(writer, value, null, valueContract, contract, member)) + continue; + + writer.WritePropertyName(propertyName, escape); + + SerializeValue(writer, value, valueContract, null, contract, member); + } + } + catch (Exception ex) + { + if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + + writer.WriteEndObject(); + + _serializeStack.RemoveAt(_serializeStack.Count - 1); + + OnSerialized(writer, contract, underlyingDictionary); + } + + private string GetPropertyName(JsonWriter writer, object name, JsonContract contract, out bool escape) + { + string propertyName; + + if (contract.ContractType == JsonContractType.Primitive) + { + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; + if (primitiveContract.TypeCode == PrimitiveTypeCode.DateTime || primitiveContract.TypeCode == PrimitiveTypeCode.DateTimeNullable) + { + escape = false; + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + DateTimeUtils.WriteDateTimeString(sw, (DateTime)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + return sw.ToString(); + } +#if !NET20 + else if (primitiveContract.TypeCode == PrimitiveTypeCode.DateTimeOffset || primitiveContract.TypeCode == PrimitiveTypeCode.DateTimeOffsetNullable) + { + escape = false; + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + DateTimeUtils.WriteDateTimeOffsetString(sw, (DateTimeOffset)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + return sw.ToString(); + } +#endif + else + { + escape = true; + return Convert.ToString(name, CultureInfo.InvariantCulture); + } + } + else if (TryConvertToString(name, name.GetType(), out propertyName)) + { + escape = true; + return propertyName; + } + else + { + escape = true; + return name.ToString(); + } + } + + private void HandleError(JsonWriter writer, int initialDepth) + { + ClearErrorContext(); + + if (writer.WriteState == WriteState.Property) + writer.WriteNull(); + + while (writer.Top > initialDepth) + { + writer.WriteEnd(); + } + } + + private bool ShouldSerialize(JsonWriter writer, JsonProperty property, object target) + { + if (property.ShouldSerialize == null) + return true; + + bool shouldSerialize = property.ShouldSerialize(target); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "ShouldSerialize result for property '{0}' on {1}: {2}".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, property.DeclaringType, shouldSerialize)), null); + + return shouldSerialize; + } + + private bool IsSpecified(JsonWriter writer, JsonProperty property, object target) + { + if (property.GetIsSpecified == null) + return true; + + bool isSpecified = property.GetIsSpecified(target); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "IsSpecified result for property '{0}' on {1}: {2}".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, property.DeclaringType, isSpecified)), null); + + return isSpecified; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonSerializerProxy.cs b/Libs/JsonNet/Serialization/JsonSerializerProxy.cs new file mode 100644 index 0000000..a3f3a32 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonSerializerProxy.cs @@ -0,0 +1,255 @@ +#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.Globalization; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerProxy : JsonSerializer + { + private readonly JsonSerializerInternalReader _serializerReader; + private readonly JsonSerializerInternalWriter _serializerWriter; + private readonly JsonSerializer _serializer; + + public override event EventHandler Error + { + add { _serializer.Error += value; } + remove { _serializer.Error -= value; } + } + + public override IReferenceResolver ReferenceResolver + { + get { return _serializer.ReferenceResolver; } + set { _serializer.ReferenceResolver = value; } + } + + public override ITraceWriter TraceWriter + { + get { return _serializer.TraceWriter; } + set { _serializer.TraceWriter = value; } + } + + public override JsonConverterCollection Converters + { + get { return _serializer.Converters; } + } + + public override DefaultValueHandling DefaultValueHandling + { + get { return _serializer.DefaultValueHandling; } + set { _serializer.DefaultValueHandling = value; } + } + + public override IContractResolver ContractResolver + { + get { return _serializer.ContractResolver; } + set { _serializer.ContractResolver = value; } + } + + public override MissingMemberHandling MissingMemberHandling + { + get { return _serializer.MissingMemberHandling; } + set { _serializer.MissingMemberHandling = value; } + } + + public override NullValueHandling NullValueHandling + { + get { return _serializer.NullValueHandling; } + set { _serializer.NullValueHandling = value; } + } + + public override ObjectCreationHandling ObjectCreationHandling + { + get { return _serializer.ObjectCreationHandling; } + set { _serializer.ObjectCreationHandling = value; } + } + + public override ReferenceLoopHandling ReferenceLoopHandling + { + get { return _serializer.ReferenceLoopHandling; } + set { _serializer.ReferenceLoopHandling = value; } + } + + public override PreserveReferencesHandling PreserveReferencesHandling + { + get { return _serializer.PreserveReferencesHandling; } + set { _serializer.PreserveReferencesHandling = value; } + } + + public override TypeNameHandling TypeNameHandling + { + get { return _serializer.TypeNameHandling; } + set { _serializer.TypeNameHandling = value; } + } + + public override MetadataPropertyHandling MetadataPropertyHandling + { + get { return _serializer.MetadataPropertyHandling; } + set { _serializer.MetadataPropertyHandling = value; } + } + + public override FormatterAssemblyStyle TypeNameAssemblyFormat + { + get { return _serializer.TypeNameAssemblyFormat; } + set { _serializer.TypeNameAssemblyFormat = value; } + } + + public override ConstructorHandling ConstructorHandling + { + get { return _serializer.ConstructorHandling; } + set { _serializer.ConstructorHandling = value; } + } + + public override SerializationBinder Binder + { + get { return _serializer.Binder; } + set { _serializer.Binder = value; } + } + + public override StreamingContext Context + { + get { return _serializer.Context; } + set { _serializer.Context = value; } + } + + public override Formatting Formatting + { + get { return _serializer.Formatting; } + set { _serializer.Formatting = value; } + } + + public override DateFormatHandling DateFormatHandling + { + get { return _serializer.DateFormatHandling; } + set { _serializer.DateFormatHandling = value; } + } + + public override DateTimeZoneHandling DateTimeZoneHandling + { + get { return _serializer.DateTimeZoneHandling; } + set { _serializer.DateTimeZoneHandling = value; } + } + + public override DateParseHandling DateParseHandling + { + get { return _serializer.DateParseHandling; } + set { _serializer.DateParseHandling = value; } + } + + public override FloatFormatHandling FloatFormatHandling + { + get { return _serializer.FloatFormatHandling; } + set { _serializer.FloatFormatHandling = value; } + } + + public override FloatParseHandling FloatParseHandling + { + get { return _serializer.FloatParseHandling; } + set { _serializer.FloatParseHandling = value; } + } + + public override StringEscapeHandling StringEscapeHandling + { + get { return _serializer.StringEscapeHandling; } + set { _serializer.StringEscapeHandling = value; } + } + + public override string DateFormatString + { + get { return _serializer.DateFormatString; } + set { _serializer.DateFormatString = value; } + } + + public override CultureInfo Culture + { + get { return _serializer.Culture; } + set { _serializer.Culture = value; } + } + + public override int? MaxDepth + { + get { return _serializer.MaxDepth; } + set { _serializer.MaxDepth = value; } + } + + public override bool CheckAdditionalContent + { + get { return _serializer.CheckAdditionalContent; } + set { _serializer.CheckAdditionalContent = value; } + } + + internal JsonSerializerInternalBase GetInternalSerializer() + { + if (_serializerReader != null) + return _serializerReader; + else + return _serializerWriter; + } + + public JsonSerializerProxy(JsonSerializerInternalReader serializerReader) + { + ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader"); + + _serializerReader = serializerReader; + _serializer = serializerReader.Serializer; + } + + public JsonSerializerProxy(JsonSerializerInternalWriter serializerWriter) + { + ValidationUtils.ArgumentNotNull(serializerWriter, "serializerWriter"); + + _serializerWriter = serializerWriter; + _serializer = serializerWriter.Serializer; + } + + internal override object DeserializeInternal(JsonReader reader, Type objectType) + { + if (_serializerReader != null) + return _serializerReader.Deserialize(reader, objectType, false); + else + return _serializer.Deserialize(reader, objectType); + } + + internal override void PopulateInternal(JsonReader reader, object target) + { + if (_serializerReader != null) + _serializerReader.Populate(reader, target); + else + _serializer.Populate(reader, target); + } + + internal override void SerializeInternal(JsonWriter jsonWriter, object value, Type rootType) + { + if (_serializerWriter != null) + _serializerWriter.Serialize(jsonWriter, value, rootType); + else + _serializer.Serialize(jsonWriter, value); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonStringContract.cs b/Libs/JsonNet/Serialization/JsonStringContract.cs new file mode 100644 index 0000000..be096bc --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonStringContract.cs @@ -0,0 +1,45 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonStringContract : JsonPrimitiveContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonStringContract(Type underlyingType) + : base(underlyingType) + { + ContractType = JsonContractType.String; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/JsonTypeReflector.cs b/Libs/JsonNet/Serialization/JsonTypeReflector.cs new file mode 100644 index 0000000..fa10863 --- /dev/null +++ b/Libs/JsonNet/Serialization/JsonTypeReflector.cs @@ -0,0 +1,430 @@ +#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.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Security; +#if !(NETFX_CORE || PORTABLE || PORTABLE40) +using System.Security.Permissions; +#endif +using Newtonsoft.Json.Utilities; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using System.Runtime.Serialization; + +namespace Newtonsoft.Json.Serialization +{ + internal static class JsonTypeReflector + { + private static bool? _dynamicCodeGeneration; + private static bool? _fullyTrusted; + + public const string IdPropertyName = "$id"; + public const string RefPropertyName = "$ref"; + public const string TypePropertyName = "$type"; + public const string ValuePropertyName = "$value"; + public const string ArrayValuesPropertyName = "$values"; + + public const string ShouldSerializePrefix = "ShouldSerialize"; + public const string SpecifiedPostfix = "Specified"; + + private static readonly ThreadSafeStore> JsonConverterCreatorCache = + new ThreadSafeStore>(GetJsonConverterCreator); + +#if !(NET20 || NETFX_CORE) + private static readonly ThreadSafeStore AssociatedMetadataTypesCache = new ThreadSafeStore(GetAssociateMetadataTypeFromAttribute); + private static ReflectionObject _metadataTypeAttributeReflectionObject; +#endif + + public static T GetCachedAttribute(object attributeProvider) where T : Attribute + { + return CachedAttributeGetter.GetAttribute(attributeProvider); + } + +#if !NET20 + public static DataContractAttribute GetDataContractAttribute(Type type) + { + // DataContractAttribute does not have inheritance + Type currentType = type; + + while (currentType != null) + { + DataContractAttribute result = CachedAttributeGetter.GetAttribute(currentType); + if (result != null) + return result; + + currentType = currentType.BaseType(); + } + + return null; + } + + public static DataMemberAttribute GetDataMemberAttribute(MemberInfo memberInfo) + { + // DataMemberAttribute does not have inheritance + + // can't override a field + if (memberInfo.MemberType() == MemberTypes.Field) + return CachedAttributeGetter.GetAttribute(memberInfo); + + // search property and then search base properties if nothing is returned and the property is virtual + PropertyInfo propertyInfo = (PropertyInfo)memberInfo; + DataMemberAttribute result = CachedAttributeGetter.GetAttribute(propertyInfo); + if (result == null) + { + if (propertyInfo.IsVirtual()) + { + Type currentType = propertyInfo.DeclaringType; + + while (result == null && currentType != null) + { + PropertyInfo baseProperty = (PropertyInfo)ReflectionUtils.GetMemberInfoFromType(currentType, propertyInfo); + if (baseProperty != null && baseProperty.IsVirtual()) + result = CachedAttributeGetter.GetAttribute(baseProperty); + + currentType = currentType.BaseType(); + } + } + } + + return result; + } +#endif + + public static MemberSerialization GetObjectMemberSerialization(Type objectType, bool ignoreSerializableAttribute) + { + JsonObjectAttribute objectAttribute = GetCachedAttribute(objectType); + if (objectAttribute != null) + return objectAttribute.MemberSerialization; + +#if !NET20 + DataContractAttribute dataContractAttribute = GetDataContractAttribute(objectType); + if (dataContractAttribute != null) + return MemberSerialization.OptIn; +#endif + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (!ignoreSerializableAttribute) + { + SerializableAttribute serializableAttribute = GetCachedAttribute(objectType); + if (serializableAttribute != null) + return MemberSerialization.Fields; + } +#endif + + // the default + return MemberSerialization.OptOut; + } + + public static JsonConverter GetJsonConverter(object attributeProvider) + { + JsonConverterAttribute converterAttribute = GetCachedAttribute(attributeProvider); + + if (converterAttribute != null) + { + Func creator = JsonConverterCreatorCache.Get(converterAttribute.ConverterType); + if (creator != null) + return creator(converterAttribute.ConverterParameters); + } + + return null; + } + + /// + /// Lookup and create an instance of the JsonConverter type described by the argument. + /// + /// The JsonConverter type to create. + /// Optional arguments to pass to an initializing constructor of the JsonConverter. + /// If null, the default constructor is used. + public static JsonConverter CreateJsonConverterInstance(Type converterType, object[] converterArgs) + { + Func converterCreator = JsonConverterCreatorCache.Get(converterType); + return converterCreator(converterArgs); + } + + /// + /// Create a factory function that can be used to create instances of a JsonConverter described by the + /// argument type. The returned function can then be used to either invoke the converter's default ctor, or any + /// parameterized constructors by way of an object array. + /// + private static Func GetJsonConverterCreator(Type converterType) + { + Func defaultConstructor = (ReflectionUtils.HasDefaultConstructor(converterType, false)) + ? ReflectionDelegateFactory.CreateDefaultConstructor(converterType) + : null; + + return (parameters) => + { + try + { + if (parameters != null) + { + ObjectConstructor parameterizedConstructor = null; + Type[] paramTypes = parameters.Select(param => param.GetType()).ToArray(); + ConstructorInfo parameterizedConstructorInfo = converterType.GetConstructor(paramTypes); + + if (null != parameterizedConstructorInfo) + { + parameterizedConstructor = ReflectionDelegateFactory.CreateParametrizedConstructor(parameterizedConstructorInfo); + return (JsonConverter)parameterizedConstructor(parameters); + } + else + { + throw new JsonException("No matching parameterized constructor found for '{0}'.".FormatWith(CultureInfo.InvariantCulture, converterType)); + } + } + + if (defaultConstructor == null) + throw new JsonException("No parameterless constructor defined for '{0}'.".FormatWith(CultureInfo.InvariantCulture, converterType)); + + return (JsonConverter)defaultConstructor(); + } + catch (Exception ex) + { + throw new JsonException("Error creating '{0}'.".FormatWith(CultureInfo.InvariantCulture, converterType), ex); + } + }; + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + public static TypeConverter GetTypeConverter(Type type) + { + return TypeDescriptor.GetConverter(type); + } +#endif + +#if !(NET20 || NETFX_CORE) + private static Type GetAssociatedMetadataType(Type type) + { + return AssociatedMetadataTypesCache.Get(type); + } + + private static Type GetAssociateMetadataTypeFromAttribute(Type type) + { + object[] customAttributes; +#if !PORTABLE + customAttributes = type.GetCustomAttributes(false); +#else + customAttributes = type.GetTypeInfo().GetCustomAttributes(false).Cast().ToArray(); +#endif + + foreach (var attribute in customAttributes) + { + Type attributeType = attribute.GetType(); + + // only test on attribute type name + // attribute assembly could change because of type forwarding, etc + if (string.Equals(attributeType.FullName, "System.ComponentModel.DataAnnotations.MetadataTypeAttribute", StringComparison.Ordinal)) + { + const string metadataClassTypeName = "MetadataClassType"; + + if (_metadataTypeAttributeReflectionObject == null) + _metadataTypeAttributeReflectionObject = ReflectionObject.Create(attributeType, metadataClassTypeName); + + return (Type)_metadataTypeAttributeReflectionObject.GetValue(attribute, metadataClassTypeName); + } + } + + return null; + } +#endif + + private static T GetAttribute(Type type) where T : Attribute + { + T attribute; + +#if !(NET20 || NETFX_CORE) + Type metadataType = GetAssociatedMetadataType(type); + if (metadataType != null) + { + attribute = ReflectionUtils.GetAttribute(metadataType, true); + if (attribute != null) + return attribute; + } +#endif + + attribute = ReflectionUtils.GetAttribute(type, true); + if (attribute != null) + return attribute; + + foreach (Type typeInterface in type.GetInterfaces()) + { + attribute = ReflectionUtils.GetAttribute(typeInterface, true); + if (attribute != null) + return attribute; + } + + return null; + } + + private static T GetAttribute(MemberInfo memberInfo) where T : Attribute + { + T attribute; + +#if !(NET20 || NETFX_CORE) + Type metadataType = GetAssociatedMetadataType(memberInfo.DeclaringType); + if (metadataType != null) + { + MemberInfo metadataTypeMemberInfo = ReflectionUtils.GetMemberInfoFromType(metadataType, memberInfo); + + if (metadataTypeMemberInfo != null) + { + attribute = ReflectionUtils.GetAttribute(metadataTypeMemberInfo, true); + if (attribute != null) + return attribute; + } + } +#endif + + attribute = ReflectionUtils.GetAttribute(memberInfo, true); + if (attribute != null) + return attribute; + + if (memberInfo.DeclaringType != null) + { + foreach (Type typeInterface in memberInfo.DeclaringType.GetInterfaces()) + { + MemberInfo interfaceTypeMemberInfo = ReflectionUtils.GetMemberInfoFromType(typeInterface, memberInfo); + + if (interfaceTypeMemberInfo != null) + { + attribute = ReflectionUtils.GetAttribute(interfaceTypeMemberInfo, true); + if (attribute != null) + return attribute; + } + } + } + + return null; + } + + public static T GetAttribute(object provider) where T : Attribute + { + Type type = provider as Type; + if (type != null) + return GetAttribute(type); + + MemberInfo memberInfo = provider as MemberInfo; + if (memberInfo != null) + return GetAttribute(memberInfo); + + return ReflectionUtils.GetAttribute(provider, true); + } + +#if DEBUG + internal static void SetFullyTrusted(bool fullyTrusted) + { + _fullyTrusted = fullyTrusted; + } + + internal static void SetDynamicCodeGeneration(bool dynamicCodeGeneration) + { + _dynamicCodeGeneration = dynamicCodeGeneration; + } +#endif + + public static bool DynamicCodeGeneration + { +#if !(NET20 || NET35 || NETFX_CORE || PORTABLE) + [SecuritySafeCritical] +#endif + get + { + if (_dynamicCodeGeneration == null) + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + try + { + new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Demand(); + new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess).Demand(); + new SecurityPermission(SecurityPermissionFlag.SkipVerification).Demand(); + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + new SecurityPermission(PermissionState.Unrestricted).Demand(); + _dynamicCodeGeneration = true; + } + catch (Exception) + { + _dynamicCodeGeneration = false; + } +#else + _dynamicCodeGeneration = false; +#endif + } + + return _dynamicCodeGeneration.Value; + } + } + + public static bool FullyTrusted + { + get + { + if (_fullyTrusted == null) + { +#if (NETFX_CORE || PORTABLE || PORTABLE40) + _fullyTrusted = false; +#elif !(NET20 || NET35 || PORTABLE40) + AppDomain appDomain = AppDomain.CurrentDomain; + + _fullyTrusted = appDomain.IsHomogenous && appDomain.IsFullyTrusted; +#else + try + { + new SecurityPermission(PermissionState.Unrestricted).Demand(); + _fullyTrusted = true; + } + catch (Exception) + { + _fullyTrusted = false; + } +#endif + } + + return _fullyTrusted.Value; + } + } + + public static ReflectionDelegateFactory ReflectionDelegateFactory + { + get + { +#if !(PORTABLE40 || PORTABLE || NETFX_CORE) + if (DynamicCodeGeneration) + return DynamicReflectionDelegateFactory.Instance; + + return LateBoundReflectionDelegateFactory.Instance; +#else + return ExpressionReflectionDelegateFactory.Instance; +#endif + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/MemoryTraceWriter.cs b/Libs/JsonNet/Serialization/MemoryTraceWriter.cs new file mode 100644 index 0000000..f8ed8ba --- /dev/null +++ b/Libs/JsonNet/Serialization/MemoryTraceWriter.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a trace writer that writes to memory. When the trace message limit is + /// reached then old trace messages will be removed as new messages are added. + /// + public class MemoryTraceWriter : ITraceWriter + { + private readonly Queue _traceMessages; + + /// + /// Gets the that will be used to filter the trace messages passed to the writer. + /// For example a filter level of Info will exclude Verbose messages and include Info, + /// Warning and Error messages. + /// + /// + /// The that will be used to filter the trace messages passed to the writer. + /// + public TraceLevel LevelFilter { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public MemoryTraceWriter() + { + LevelFilter = TraceLevel.Verbose; + _traceMessages = new Queue(); + } + + /// + /// Writes the specified trace level, message and optional exception. + /// + /// The at which to write this trace. + /// The trace message. + /// The trace exception. This parameter is optional. + public void Trace(TraceLevel level, string message, Exception ex) + { + string traceMessage = DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff", CultureInfo.InvariantCulture) + " " + level.ToString("g") + " " + message; + + if (_traceMessages.Count >= 1000) + _traceMessages.Dequeue(); + + _traceMessages.Enqueue(traceMessage); + } + + /// + /// Returns an enumeration of the most recent trace messages. + /// + /// An enumeration of the most recent trace messages. + public IEnumerable GetTraceMessages() + { + return _traceMessages; + } + + /// + /// Returns a of the most recent trace messages. + /// + /// + /// A of the most recent trace messages. + /// + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + foreach (string traceMessage in _traceMessages) + { + if (sb.Length > 0) + sb.AppendLine(); + + sb.Append(traceMessage); + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ObjectConstructor.cs b/Libs/JsonNet/Serialization/ObjectConstructor.cs new file mode 100644 index 0000000..380674f --- /dev/null +++ b/Libs/JsonNet/Serialization/ObjectConstructor.cs @@ -0,0 +1,33 @@ +#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 + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a method that constructs an object. + /// + /// The object type to create. + public delegate object ObjectConstructor(params object[] args); +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/OnErrorAttribute.cs b/Libs/JsonNet/Serialization/OnErrorAttribute.cs new file mode 100644 index 0000000..cb33296 --- /dev/null +++ b/Libs/JsonNet/Serialization/OnErrorAttribute.cs @@ -0,0 +1,37 @@ +#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; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// When applied to a method, specifies that the method is called when an error occurs serializing an object. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnErrorAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/ReflectionValueProvider.cs b/Libs/JsonNet/Serialization/ReflectionValueProvider.cs new file mode 100644 index 0000000..0fcc11f --- /dev/null +++ b/Libs/JsonNet/Serialization/ReflectionValueProvider.cs @@ -0,0 +1,84 @@ +#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.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using reflection. + /// + public class ReflectionValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public ReflectionValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + ReflectionUtils.SetMemberValue(_memberInfo, target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + return ReflectionUtils.GetMemberValue(_memberInfo, target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/TraceJsonReader.cs b/Libs/JsonNet/Serialization/TraceJsonReader.cs new file mode 100644 index 0000000..4b12c0d --- /dev/null +++ b/Libs/JsonNet/Serialization/TraceJsonReader.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + internal class TraceJsonReader : JsonReader, IJsonLineInfo + { + private readonly JsonReader _innerReader; + private readonly JsonTextWriter _textWriter; + private readonly StringWriter _sw; + + public TraceJsonReader(JsonReader innerReader) + { + _innerReader = innerReader; + + _sw = new StringWriter(CultureInfo.InvariantCulture); + _textWriter = new JsonTextWriter(_sw); + _textWriter.Formatting = Formatting.Indented; + } + + public string GetJson() + { + return _sw.ToString(); + } + + public override bool Read() + { + var value = _innerReader.Read(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + + public override int? ReadAsInt32() + { + var value = _innerReader.ReadAsInt32(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + + public override string ReadAsString() + { + var value = _innerReader.ReadAsString(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + + public override byte[] ReadAsBytes() + { + var value = _innerReader.ReadAsBytes(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + + public override decimal? ReadAsDecimal() + { + var value = _innerReader.ReadAsDecimal(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + + public override DateTime? ReadAsDateTime() + { + var value = _innerReader.ReadAsDateTime(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } + +#if !NET20 + public override DateTimeOffset? ReadAsDateTimeOffset() + { + var value = _innerReader.ReadAsDateTimeOffset(); + _textWriter.WriteToken(_innerReader, false, false); + return value; + } +#endif + + public override int Depth + { + get { return _innerReader.Depth; } + } + + public override string Path + { + get { return _innerReader.Path; } + } + + public override char QuoteChar + { + get { return _innerReader.QuoteChar; } + protected internal set { _innerReader.QuoteChar = value; } + } + + public override JsonToken TokenType + { + get { return _innerReader.TokenType; } + } + + public override object Value + { + get { return _innerReader.Value; } + } + + public override Type ValueType + { + get { return _innerReader.ValueType; } + } + + public override void Close() + { + _innerReader.Close(); + } + + bool IJsonLineInfo.HasLineInfo() + { + IJsonLineInfo lineInfo = _innerReader as IJsonLineInfo; + return lineInfo != null && lineInfo.HasLineInfo(); + } + + int IJsonLineInfo.LineNumber + { + get + { + IJsonLineInfo lineInfo = _innerReader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LineNumber : 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + IJsonLineInfo lineInfo = _innerReader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LinePosition : 0; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Serialization/TraceJsonWriter.cs b/Libs/JsonNet/Serialization/TraceJsonWriter.cs new file mode 100644 index 0000000..cd8c4a1 --- /dev/null +++ b/Libs/JsonNet/Serialization/TraceJsonWriter.cs @@ -0,0 +1,348 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) +using System.Numerics; +#endif +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + internal class TraceJsonWriter : JsonWriter + { + private readonly JsonWriter _innerWriter; + private readonly JsonTextWriter _textWriter; + private readonly StringWriter _sw; + + public TraceJsonWriter(JsonWriter innerWriter) + { + _innerWriter = innerWriter; + + _sw = new StringWriter(CultureInfo.InvariantCulture); + _textWriter = new JsonTextWriter(_sw); + _textWriter.Formatting = Formatting.Indented; + _textWriter.Culture = innerWriter.Culture; + _textWriter.DateFormatHandling = innerWriter.DateFormatHandling; + _textWriter.DateFormatString = innerWriter.DateFormatString; + _textWriter.DateTimeZoneHandling = innerWriter.DateTimeZoneHandling; + _textWriter.FloatFormatHandling = innerWriter.FloatFormatHandling; + } + + public string GetJson() + { + return _sw.ToString(); + } + + public override void WriteValue(decimal value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(bool value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(byte value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(byte? value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(char value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(byte[] value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(DateTime value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + +#if !NET20 + public override void WriteValue(DateTimeOffset value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } +#endif + + public override void WriteValue(double value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteUndefined() + { + _textWriter.WriteUndefined(); + _innerWriter.WriteUndefined(); + base.WriteUndefined(); + } + + public override void WriteNull() + { + _textWriter.WriteNull(); + _innerWriter.WriteNull(); + base.WriteUndefined(); + } + + public override void WriteValue(float value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(Guid value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(int value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(long value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(object value) + { +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + if (value is BigInteger) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + InternalWriteValue(JsonToken.Integer); + } + else +#endif + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + } + + public override void WriteValue(sbyte value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(short value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(string value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(TimeSpan value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(uint value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(ulong value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(Uri value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteValue(ushort value) + { + _textWriter.WriteValue(value); + _innerWriter.WriteValue(value); + base.WriteValue(value); + } + + public override void WriteWhitespace(string ws) + { + _textWriter.WriteWhitespace(ws); + _innerWriter.WriteWhitespace(ws); + base.WriteWhitespace(ws); + } + + //protected override void WriteValueDelimiter() + //{ + // _textWriter.WriteValueDelimiter(); + // _innerWriter.WriteValueDelimiter(); + // base.WriteValueDelimiter(); + //} + + //protected override void WriteIndent() + //{ + // base.WriteIndent(); + //} + + public override void WriteComment(string text) + { + _textWriter.WriteComment(text); + _innerWriter.WriteComment(text); + base.WriteComment(text); + } + + //public override void WriteEnd() + //{ + // _textWriter.WriteEnd(); + // _innerWriter.WriteEnd(); + // base.WriteEnd(); + //} + + //protected override void WriteEnd(JsonToken token) + //{ + // base.WriteEnd(token); + //} + + public override void WriteStartArray() + { + _textWriter.WriteStartArray(); + _innerWriter.WriteStartArray(); + base.WriteStartArray(); + } + + public override void WriteEndArray() + { + _textWriter.WriteEndArray(); + _innerWriter.WriteEndArray(); + base.WriteEndArray(); + } + + public override void WriteStartConstructor(string name) + { + _textWriter.WriteStartConstructor(name); + _innerWriter.WriteStartConstructor(name); + base.WriteStartConstructor(name); + } + + public override void WriteEndConstructor() + { + _textWriter.WriteEndConstructor(); + _innerWriter.WriteEndConstructor(); + base.WriteEndConstructor(); + } + + public override void WritePropertyName(string name) + { + _textWriter.WritePropertyName(name); + _innerWriter.WritePropertyName(name); + base.WritePropertyName(name); + } + + public override void WritePropertyName(string name, bool escape) + { + _textWriter.WritePropertyName(name, escape); + _innerWriter.WritePropertyName(name, escape); + + // method with escape will error + base.WritePropertyName(name); + } + + public override void WriteStartObject() + { + _textWriter.WriteStartObject(); + _innerWriter.WriteStartObject(); + base.WriteStartObject(); + } + + public override void WriteEndObject() + { + _textWriter.WriteEndObject(); + _innerWriter.WriteEndObject(); + base.WriteEndObject(); + } + + public override void WriteRaw(string json) + { + _textWriter.WriteRaw(json); + _innerWriter.WriteRaw(json); + base.WriteRaw(json); + } + + public override void WriteRawValue(string json) + { + _textWriter.WriteRawValue(json); + _innerWriter.WriteRawValue(json); + base.WriteRawValue(json); + } + + //protected override void WriteIndentSpace() + //{ + // _textWriter.WriteIndentSpace(); + // _innerWriter.WriteIndentSpace(); + // base.WriteIndentSpace(); + //} + + public override void Close() + { + _textWriter.Close(); + _innerWriter.Close(); + base.Close(); + } + + public override void Flush() + { + _textWriter.Flush(); + _innerWriter.Flush(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/SerializationBinder.cs b/Libs/JsonNet/SerializationBinder.cs new file mode 100644 index 0000000..0489ef1 --- /dev/null +++ b/Libs/JsonNet/SerializationBinder.cs @@ -0,0 +1,35 @@ + +#if PocketPC || NETFX_CORE || PORTABLE40 || PORTABLE +using System; +using System.Reflection; + +namespace Newtonsoft.Json +{ + /// + /// Allows users to control class loading and mandate what class to load. + /// + public abstract class SerializationBinder + { + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object + /// The type of the object the formatter creates a new instance of. + public abstract Type BindToType(string assemblyName, string typeName); + + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// The type of the object the formatter creates a new instance of. + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + public virtual void BindToName(Type serializedType, out string assemblyName, out string typeName) + { + assemblyName = null; + typeName = null; + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/StringEscapeHandling.cs b/Libs/JsonNet/StringEscapeHandling.cs new file mode 100644 index 0000000..13a332b --- /dev/null +++ b/Libs/JsonNet/StringEscapeHandling.cs @@ -0,0 +1,48 @@ +#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 + +namespace Newtonsoft.Json +{ + /// + /// Specifies how strings are escaped when writing JSON text. + /// + public enum StringEscapeHandling + { + /// + /// Only control characters (e.g. newline) are escaped. + /// + Default, + + /// + /// All non-ASCII and control characters (e.g. newline) are escaped. + /// + EscapeNonAscii, + + /// + /// HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped. + /// + EscapeHtml + } +} \ No newline at end of file diff --git a/Libs/JsonNet/TraceLevel.cs b/Libs/JsonNet/TraceLevel.cs new file mode 100644 index 0000000..e80d532 --- /dev/null +++ b/Libs/JsonNet/TraceLevel.cs @@ -0,0 +1,39 @@ + +#if (NETFX_CORE || PORTABLE40 || PORTABLE) +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies what messages to output for the class. + /// + public enum TraceLevel + { + /// + /// Output no tracing and debugging messages. + /// + Off, + + /// + /// Output error-handling messages. + /// + Error, + + /// + /// Output warnings and error-handling messages. + /// + Warning, + + /// + /// Output informational messages, warnings, and error-handling messages. + /// + Info, + + /// + /// Output all debugging and tracing messages. + /// + Verbose + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/TypeNameHandling.cs b/Libs/JsonNet/TypeNameHandling.cs new file mode 100644 index 0000000..deb0c88 --- /dev/null +++ b/Libs/JsonNet/TypeNameHandling.cs @@ -0,0 +1,61 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Specifies type name handling options for the . + /// + [Flags] + public enum TypeNameHandling + { + /// + /// Do not include the .NET type name when serializing types. + /// + None = 0, + + /// + /// Include the .NET type name when serializing into a JSON object structure. + /// + Objects = 1, + + /// + /// Include the .NET type name when serializing into a JSON array structure. + /// + Arrays = 2, + + /// + /// Always include the .NET type name when serializing. + /// + All = Objects | Arrays, + + /// + /// Include the .NET type name when the type of the object being serialized is not the same as its declared type. + /// + Auto = 4 + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/Base64Encoder.cs b/Libs/JsonNet/Utilities/Base64Encoder.cs new file mode 100644 index 0000000..5867e7a --- /dev/null +++ b/Libs/JsonNet/Utilities/Base64Encoder.cs @@ -0,0 +1,120 @@ +#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.IO; + +namespace Newtonsoft.Json.Utilities +{ + internal class Base64Encoder + { + private const int Base64LineSize = 76; + private const int LineSizeInBytes = 57; + + private readonly char[] _charsLine = new char[Base64LineSize]; + private readonly TextWriter _writer; + + private byte[] _leftOverBytes; + private int _leftOverBytesCount; + + public Base64Encoder(TextWriter writer) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + _writer = writer; + } + + public void Encode(byte[] buffer, int index, int count) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if (count > (buffer.Length - index)) + throw new ArgumentOutOfRangeException("count"); + + if (_leftOverBytesCount > 0) + { + int leftOverBytesCount = _leftOverBytesCount; + while (leftOverBytesCount < 3 && count > 0) + { + _leftOverBytes[leftOverBytesCount++] = buffer[index++]; + count--; + } + if (count == 0 && leftOverBytesCount < 3) + { + _leftOverBytesCount = leftOverBytesCount; + return; + } + int num2 = Convert.ToBase64CharArray(_leftOverBytes, 0, 3, _charsLine, 0); + WriteChars(_charsLine, 0, num2); + } + _leftOverBytesCount = count % 3; + if (_leftOverBytesCount > 0) + { + count -= _leftOverBytesCount; + if (_leftOverBytes == null) + { + _leftOverBytes = new byte[3]; + } + for (int i = 0; i < _leftOverBytesCount; i++) + { + _leftOverBytes[i] = buffer[(index + count) + i]; + } + } + int num4 = index + count; + int length = LineSizeInBytes; + while (index < num4) + { + if ((index + length) > num4) + { + length = num4 - index; + } + int num6 = Convert.ToBase64CharArray(buffer, index, length, _charsLine, 0); + WriteChars(_charsLine, 0, num6); + index += length; + } + } + + public void Flush() + { + if (_leftOverBytesCount > 0) + { + int count = Convert.ToBase64CharArray(_leftOverBytes, 0, _leftOverBytesCount, _charsLine, 0); + WriteChars(_charsLine, 0, count); + _leftOverBytesCount = 0; + } + } + + private void WriteChars(char[] chars, int index, int count) + { + _writer.Write(chars, index, count); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/BidirectionalDictionary.cs b/Libs/JsonNet/Utilities/BidirectionalDictionary.cs new file mode 100644 index 0000000..2711adf --- /dev/null +++ b/Libs/JsonNet/Utilities/BidirectionalDictionary.cs @@ -0,0 +1,93 @@ +#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.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal class BidirectionalDictionary + { + private readonly IDictionary _firstToSecond; + private readonly IDictionary _secondToFirst; + private readonly string _duplicateFirstErrorMessage; + private readonly string _duplicateSecondErrorMessage; + + public BidirectionalDictionary() + : this(EqualityComparer.Default, EqualityComparer.Default) + { + } + + public BidirectionalDictionary(IEqualityComparer firstEqualityComparer, IEqualityComparer secondEqualityComparer) + : this( + firstEqualityComparer, + secondEqualityComparer, + "Duplicate item already exists for '{0}'.", + "Duplicate item already exists for '{0}'.") + { + } + + public BidirectionalDictionary(IEqualityComparer firstEqualityComparer, IEqualityComparer secondEqualityComparer, + string duplicateFirstErrorMessage, string duplicateSecondErrorMessage) + { + _firstToSecond = new Dictionary(firstEqualityComparer); + _secondToFirst = new Dictionary(secondEqualityComparer); + _duplicateFirstErrorMessage = duplicateFirstErrorMessage; + _duplicateSecondErrorMessage = duplicateSecondErrorMessage; + } + + public void Set(TFirst first, TSecond second) + { + TFirst existingFirst; + TSecond existingSecond; + + if (_firstToSecond.TryGetValue(first, out existingSecond)) + { + if (!existingSecond.Equals(second)) + throw new ArgumentException(_duplicateFirstErrorMessage.FormatWith(CultureInfo.InvariantCulture, first)); + } + + if (_secondToFirst.TryGetValue(second, out existingFirst)) + { + if (!existingFirst.Equals(first)) + throw new ArgumentException(_duplicateSecondErrorMessage.FormatWith(CultureInfo.InvariantCulture, second)); + } + + _firstToSecond.Add(first, second); + _secondToFirst.Add(second, first); + } + + public bool TryGetByFirst(TFirst first, out TSecond second) + { + return _firstToSecond.TryGetValue(first, out second); + } + + public bool TryGetBySecond(TSecond second, out TFirst first) + { + return _secondToFirst.TryGetValue(second, out first); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/CollectionUtils.cs b/Libs/JsonNet/Utilities/CollectionUtils.cs new file mode 100644 index 0000000..9236173 --- /dev/null +++ b/Libs/JsonNet/Utilities/CollectionUtils.cs @@ -0,0 +1,298 @@ +#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.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Collections; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using System.Globalization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class CollectionUtils + { + /// + /// Determines whether the collection is null or empty. + /// + /// The collection. + /// + /// true if the collection is null or empty; otherwise, false. + /// + public static bool IsNullOrEmpty(ICollection collection) + { + if (collection != null) + { + return (collection.Count == 0); + } + return true; + } + + /// + /// Adds the elements of the specified collection to the specified generic IList. + /// + /// The list to add to. + /// The collection of elements to add. + public static void AddRange(this IList initial, IEnumerable collection) + { + if (initial == null) + throw new ArgumentNullException("initial"); + + if (collection == null) + return; + + foreach (T value in collection) + { + initial.Add(value); + } + } + +#if (NET20 || NET35 || PORTABLE40) + public static void AddRange(this IList initial, IEnumerable collection) + { + ValidationUtils.ArgumentNotNull(initial, "initial"); + + // because earlier versions of .NET didn't support covariant generics + initial.AddRange(collection.Cast()); + } +#endif + + public static bool IsDictionaryType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (typeof(IDictionary).IsAssignableFrom(type)) + return true; + if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IDictionary<,>))) + return true; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IReadOnlyDictionary<,>))) + return true; +#endif + + return false; + } + + public static ConstructorInfo ResolveEnumerableCollectionConstructor(Type collectionType, Type collectionItemType) + { + Type genericEnumerable = typeof(IEnumerable<>).MakeGenericType(collectionItemType); + ConstructorInfo match = null; + + foreach (ConstructorInfo constructor in collectionType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)) + { + IList parameters = constructor.GetParameters(); + + if (parameters.Count == 1) + { + if (genericEnumerable == parameters[0].ParameterType) + { + // exact match + match = constructor; + break; + } + + // incase we can't find an exact match, use first inexact + if (match == null) + { + if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType)) + match = constructor; + } + } + } + + return match; + } + + public static bool AddDistinct(this IList list, T value) + { + return list.AddDistinct(value, EqualityComparer.Default); + } + + public static bool AddDistinct(this IList list, T value, IEqualityComparer comparer) + { + if (list.ContainsValue(value, comparer)) + return false; + + list.Add(value); + return true; + } + + // this is here because LINQ Bridge doesn't support Contains with IEqualityComparer + public static bool ContainsValue(this IEnumerable source, TSource value, IEqualityComparer comparer) + { + if (comparer == null) + comparer = EqualityComparer.Default; + + if (source == null) + throw new ArgumentNullException("source"); + + foreach (TSource local in source) + { + if (comparer.Equals(local, value)) + return true; + } + + return false; + } + + public static bool AddRangeDistinct(this IList list, IEnumerable values, IEqualityComparer comparer) + { + bool allAdded = true; + foreach (T value in values) + { + if (!list.AddDistinct(value, comparer)) + allAdded = false; + } + + return allAdded; + } + + public static int IndexOf(this IEnumerable collection, Func predicate) + { + int index = 0; + foreach (T value in collection) + { + if (predicate(value)) + return index; + + index++; + } + + return -1; + } + + /// + /// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer. + /// + /// The type of the elements of source. + /// A sequence in which to locate a value. + /// The object to locate in the sequence + /// An equality comparer to compare values. + /// The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, 1. + public static int IndexOf(this IEnumerable list, TSource value, IEqualityComparer comparer) + { + int index = 0; + foreach (TSource item in list) + { + if (comparer.Equals(item, value)) + { + return index; + } + index++; + } + return -1; + } + + private static IList GetDimensions(IList values, int dimensionsCount) + { + IList dimensions = new List(); + + IList currentArray = values; + while (true) + { + dimensions.Add(currentArray.Count); + + // don't keep calculating dimensions for arrays inside the value array + if (dimensions.Count == dimensionsCount) + break; + + if (currentArray.Count == 0) + break; + + object v = currentArray[0]; + if (v is IList) + currentArray = (IList)v; + else + break; + } + + return dimensions; + } + + private static void CopyFromJaggedToMultidimensionalArray(IList values, Array multidimensionalArray, int[] indices) + { + int dimension = indices.Length; + if (dimension == multidimensionalArray.Rank) + { + multidimensionalArray.SetValue(JaggedArrayGetValue(values, indices), indices); + return; + } + + int dimensionLength = multidimensionalArray.GetLength(dimension); + IList list = (IList)JaggedArrayGetValue(values, indices); + int currentValuesLength = list.Count; + if (currentValuesLength != dimensionLength) + throw new Exception("Cannot deserialize non-cubical array as multidimensional array."); + + int[] newIndices = new int[dimension + 1]; + for (int i = 0; i < dimension; i++) + { + newIndices[i] = indices[i]; + } + + for (int i = 0; i < multidimensionalArray.GetLength(dimension); i++) + { + newIndices[dimension] = i; + CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, newIndices); + } + } + + private static object JaggedArrayGetValue(IList values, int[] indices) + { + IList currentList = values; + for (int i = 0; i < indices.Length; i++) + { + int index = indices[i]; + if (i == indices.Length - 1) + return currentList[index]; + else + currentList = (IList)currentList[index]; + } + return currentList; + } + + public static Array ToMultidimensionalArray(IList values, Type type, int rank) + { + IList dimensions = GetDimensions(values, rank); + + while (dimensions.Count < rank) + { + dimensions.Add(0); + } + + Array multidimensionalArray = Array.CreateInstance(type, dimensions.ToArray()); + CopyFromJaggedToMultidimensionalArray(values, multidimensionalArray, new int[0]); + + return multidimensionalArray; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/CollectionWrapper.cs b/Libs/JsonNet/Utilities/CollectionWrapper.cs new file mode 100644 index 0000000..2497845 --- /dev/null +++ b/Libs/JsonNet/Utilities/CollectionWrapper.cs @@ -0,0 +1,282 @@ +#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; +using System.Collections.Generic; +using System.Threading; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal interface IWrappedCollection : IList + { + object UnderlyingCollection { get; } + } + + internal class CollectionWrapper : ICollection, IWrappedCollection + { + private readonly IList _list; + private readonly ICollection _genericCollection; + private object _syncRoot; + + public CollectionWrapper(IList list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + if (list is ICollection) + _genericCollection = (ICollection)list; + else + _list = list; + } + + public CollectionWrapper(ICollection list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + _genericCollection = list; + } + + public virtual void Add(T item) + { + if (_genericCollection != null) + _genericCollection.Add(item); + else + _list.Add(item); + } + + public virtual void Clear() + { + if (_genericCollection != null) + _genericCollection.Clear(); + else + _list.Clear(); + } + + public virtual bool Contains(T item) + { + if (_genericCollection != null) + return _genericCollection.Contains(item); + else + return _list.Contains(item); + } + + public virtual void CopyTo(T[] array, int arrayIndex) + { + if (_genericCollection != null) + _genericCollection.CopyTo(array, arrayIndex); + else + _list.CopyTo(array, arrayIndex); + } + + public virtual int Count + { + get + { + if (_genericCollection != null) + return _genericCollection.Count; + else + return _list.Count; + } + } + + public virtual bool IsReadOnly + { + get + { + if (_genericCollection != null) + return _genericCollection.IsReadOnly; + else + return _list.IsReadOnly; + } + } + + public virtual bool Remove(T item) + { + if (_genericCollection != null) + { + return _genericCollection.Remove(item); + } + else + { + bool contains = _list.Contains(item); + + if (contains) + _list.Remove(item); + + return contains; + } + } + + public virtual IEnumerator GetEnumerator() + { + if (_genericCollection != null) + return _genericCollection.GetEnumerator(); + + return _list.Cast().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + if (_genericCollection != null) + return _genericCollection.GetEnumerator(); + else + return _list.GetEnumerator(); + } + + int IList.Add(object value) + { + VerifyValueType(value); + Add((T)value); + + return (Count - 1); + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + return Contains((T)value); + + return false; + } + + int IList.IndexOf(object value) + { + if (_genericCollection != null) + throw new InvalidOperationException("Wrapped ICollection does not support IndexOf."); + + if (IsCompatibleObject(value)) + return _list.IndexOf((T)value); + + return -1; + } + + void IList.RemoveAt(int index) + { + if (_genericCollection != null) + throw new InvalidOperationException("Wrapped ICollection does not support RemoveAt."); + + _list.RemoveAt(index); + } + + void IList.Insert(int index, object value) + { + if (_genericCollection != null) + throw new InvalidOperationException("Wrapped ICollection does not support Insert."); + + VerifyValueType(value); + _list.Insert(index, (T)value); + } + + bool IList.IsFixedSize + { + get + { + if (_genericCollection != null) + // ICollection only has IsReadOnly + return _genericCollection.IsReadOnly; + else + return _list.IsFixedSize; + } + } + + void IList.Remove(object value) + { + if (IsCompatibleObject(value)) + Remove((T)value); + } + + object IList.this[int index] + { + get + { + if (_genericCollection != null) + throw new InvalidOperationException("Wrapped ICollection does not support indexer."); + + return _list[index]; + } + set + { + if (_genericCollection != null) + throw new InvalidOperationException("Wrapped ICollection does not support indexer."); + + VerifyValueType(value); + _list[index] = (T)value; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + + private static void VerifyValueType(object value) + { + if (!IsCompatibleObject(value)) + throw new ArgumentException("The value '{0}' is not of type '{1}' and cannot be used in this generic collection.".FormatWith(CultureInfo.InvariantCulture, value, typeof(T)), "value"); + } + + private static bool IsCompatibleObject(object value) + { + if (!(value is T) && (value != null || (typeof(T).IsValueType() && !ReflectionUtils.IsNullableType(typeof(T))))) + return false; + + return true; + } + + public object UnderlyingCollection + { + get + { + if (_genericCollection != null) + return _genericCollection; + else + return _list; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ConvertUtils.cs b/Libs/JsonNet/Utilities/ConvertUtils.cs new file mode 100644 index 0000000..5d4627e --- /dev/null +++ b/Libs/JsonNet/Utilities/ConvertUtils.cs @@ -0,0 +1,805 @@ +#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.Globalization; +using System.ComponentModel; +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) +using System.Numerics; +#endif +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Serialization; +using System.Reflection; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) +using System.Data.SqlTypes; + +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal enum PrimitiveTypeCode + { + Empty, + Object, + Char, + CharNullable, + Boolean, + BooleanNullable, + SByte, + SByteNullable, + Int16, + Int16Nullable, + UInt16, + UInt16Nullable, + Int32, + Int32Nullable, + Byte, + ByteNullable, + UInt32, + UInt32Nullable, + Int64, + Int64Nullable, + UInt64, + UInt64Nullable, + Single, + SingleNullable, + Double, + DoubleNullable, + DateTime, + DateTimeNullable, +#if !NET20 + DateTimeOffset, + DateTimeOffsetNullable, +#endif + Decimal, + DecimalNullable, + Guid, + GuidNullable, + TimeSpan, + TimeSpanNullable, +#if !(PORTABLE || NET35 || NET20) + BigInteger, + BigIntegerNullable, +#endif + Uri, + String, + Bytes, + DBNull + } + + internal class TypeInformation + { + public Type Type { get; set; } + public PrimitiveTypeCode TypeCode { get; set; } + } + + internal enum ParseResult + { + None, + Success, + Overflow, + Invalid + } + + internal static class ConvertUtils + { + private static readonly Dictionary TypeCodeMap = + new Dictionary + { + { typeof(char), PrimitiveTypeCode.Char }, + { typeof(char?), PrimitiveTypeCode.CharNullable }, + { typeof(bool), PrimitiveTypeCode.Boolean }, + { typeof(bool?), PrimitiveTypeCode.BooleanNullable }, + { typeof(sbyte), PrimitiveTypeCode.SByte }, + { typeof(sbyte?), PrimitiveTypeCode.SByteNullable }, + { typeof(short), PrimitiveTypeCode.Int16 }, + { typeof(short?), PrimitiveTypeCode.Int16Nullable }, + { typeof(ushort), PrimitiveTypeCode.UInt16 }, + { typeof(ushort?), PrimitiveTypeCode.UInt16Nullable }, + { typeof(int), PrimitiveTypeCode.Int32 }, + { typeof(int?), PrimitiveTypeCode.Int32Nullable }, + { typeof(byte), PrimitiveTypeCode.Byte }, + { typeof(byte?), PrimitiveTypeCode.ByteNullable }, + { typeof(uint), PrimitiveTypeCode.UInt32 }, + { typeof(uint?), PrimitiveTypeCode.UInt32Nullable }, + { typeof(long), PrimitiveTypeCode.Int64 }, + { typeof(long?), PrimitiveTypeCode.Int64Nullable }, + { typeof(ulong), PrimitiveTypeCode.UInt64 }, + { typeof(ulong?), PrimitiveTypeCode.UInt64Nullable }, + { typeof(float), PrimitiveTypeCode.Single }, + { typeof(float?), PrimitiveTypeCode.SingleNullable }, + { typeof(double), PrimitiveTypeCode.Double }, + { typeof(double?), PrimitiveTypeCode.DoubleNullable }, + { typeof(DateTime), PrimitiveTypeCode.DateTime }, + { typeof(DateTime?), PrimitiveTypeCode.DateTimeNullable }, +#if !NET20 + { typeof(DateTimeOffset), PrimitiveTypeCode.DateTimeOffset }, + { typeof(DateTimeOffset?), PrimitiveTypeCode.DateTimeOffsetNullable }, +#endif + { typeof(decimal), PrimitiveTypeCode.Decimal }, + { typeof(decimal?), PrimitiveTypeCode.DecimalNullable }, + { typeof(Guid), PrimitiveTypeCode.Guid }, + { typeof(Guid?), PrimitiveTypeCode.GuidNullable }, + { typeof(TimeSpan), PrimitiveTypeCode.TimeSpan }, + { typeof(TimeSpan?), PrimitiveTypeCode.TimeSpanNullable }, +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) + { typeof(BigInteger), PrimitiveTypeCode.BigInteger }, + { typeof(BigInteger?), PrimitiveTypeCode.BigIntegerNullable }, +#endif + { typeof(Uri), PrimitiveTypeCode.Uri }, + { typeof(string), PrimitiveTypeCode.String }, + { typeof(byte[]), PrimitiveTypeCode.Bytes }, +#if !(PORTABLE || PORTABLE40 || NETFX_CORE) + { typeof(DBNull), PrimitiveTypeCode.DBNull } +#endif + }; + +#if !(NETFX_CORE || PORTABLE) + private static readonly TypeInformation[] PrimitiveTypeCodes = + { + new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty }, + new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Object }, + new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.DBNull }, + new TypeInformation { Type = typeof(bool), TypeCode = PrimitiveTypeCode.Boolean }, + new TypeInformation { Type = typeof(char), TypeCode = PrimitiveTypeCode.Char }, + new TypeInformation { Type = typeof(sbyte), TypeCode = PrimitiveTypeCode.SByte }, + new TypeInformation { Type = typeof(byte), TypeCode = PrimitiveTypeCode.Byte }, + new TypeInformation { Type = typeof(short), TypeCode = PrimitiveTypeCode.Int16 }, + new TypeInformation { Type = typeof(ushort), TypeCode = PrimitiveTypeCode.UInt16 }, + new TypeInformation { Type = typeof(int), TypeCode = PrimitiveTypeCode.Int32 }, + new TypeInformation { Type = typeof(uint), TypeCode = PrimitiveTypeCode.UInt32 }, + new TypeInformation { Type = typeof(long), TypeCode = PrimitiveTypeCode.Int64 }, + new TypeInformation { Type = typeof(ulong), TypeCode = PrimitiveTypeCode.UInt64 }, + new TypeInformation { Type = typeof(float), TypeCode = PrimitiveTypeCode.Single }, + new TypeInformation { Type = typeof(double), TypeCode = PrimitiveTypeCode.Double }, + new TypeInformation { Type = typeof(decimal), TypeCode = PrimitiveTypeCode.Decimal }, + new TypeInformation { Type = typeof(DateTime), TypeCode = PrimitiveTypeCode.DateTime }, + new TypeInformation { Type = typeof(object), TypeCode = PrimitiveTypeCode.Empty }, // no 17 in TypeCode for some reason + new TypeInformation { Type = typeof(string), TypeCode = PrimitiveTypeCode.String } + }; +#endif + + public static PrimitiveTypeCode GetTypeCode(Type t) + { + bool isEnum; + return GetTypeCode(t, out isEnum); + } + + public static PrimitiveTypeCode GetTypeCode(Type t, out bool isEnum) + { + PrimitiveTypeCode typeCode; + if (TypeCodeMap.TryGetValue(t, out typeCode)) + { + isEnum = false; + return typeCode; + } + + if (t.IsEnum()) + { + isEnum = true; + return GetTypeCode(Enum.GetUnderlyingType(t)); + } + + // performance? + if (ReflectionUtils.IsNullableType(t)) + { + Type nonNullable = Nullable.GetUnderlyingType(t); + if (nonNullable.IsEnum()) + { + Type nullableUnderlyingType = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(nonNullable)); + isEnum = true; + return GetTypeCode(nullableUnderlyingType); + } + } + + isEnum = false; + return PrimitiveTypeCode.Object; + } + +#if !(NETFX_CORE || PORTABLE) + public static TypeInformation GetTypeInformation(IConvertible convertable) + { + TypeInformation typeInformation = PrimitiveTypeCodes[(int)convertable.GetTypeCode()]; + return typeInformation; + } +#endif + + public static bool IsConvertible(Type t) + { +#if !(NETFX_CORE || PORTABLE) + return typeof(IConvertible).IsAssignableFrom(t); +#else + return ( + t == typeof(bool) || t == typeof(byte) || t == typeof(char) || t == typeof(DateTime) || t == typeof(decimal) || t == typeof(double) || t == typeof(short) || t == typeof(int) || + t == typeof(long) || t == typeof(sbyte) || t == typeof(float) || t == typeof(string) || t == typeof(ushort) || t == typeof(uint) || t == typeof(ulong) || t.IsEnum()); +#endif + } + + public static TimeSpan ParseTimeSpan(string input) + { +#if !(NET35 || NET20) + return TimeSpan.Parse(input, CultureInfo.InvariantCulture); +#else + return TimeSpan.Parse(input); +#endif + } + + internal struct TypeConvertKey : IEquatable + { + private readonly Type _initialType; + private readonly Type _targetType; + + public Type InitialType + { + get { return _initialType; } + } + + public Type TargetType + { + get { return _targetType; } + } + + public TypeConvertKey(Type initialType, Type targetType) + { + _initialType = initialType; + _targetType = targetType; + } + + public override int GetHashCode() + { + return _initialType.GetHashCode() ^ _targetType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is TypeConvertKey)) + return false; + + return Equals((TypeConvertKey)obj); + } + + public bool Equals(TypeConvertKey other) + { + return (_initialType == other._initialType && _targetType == other._targetType); + } + } + + private static readonly ThreadSafeStore> CastConverters = + new ThreadSafeStore>(CreateCastConverter); + + private static Func CreateCastConverter(TypeConvertKey t) + { + MethodInfo castMethodInfo = t.TargetType.GetMethod("op_Implicit", new[] { t.InitialType }); + if (castMethodInfo == null) + castMethodInfo = t.TargetType.GetMethod("op_Explicit", new[] { t.InitialType }); + + if (castMethodInfo == null) + return null; + + MethodCall call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(castMethodInfo); + + return o => call(null, o); + } + +#if !(NET20 || NET35 || PORTABLE || PORTABLE40) + internal static BigInteger ToBigInteger(object value) + { + if (value is BigInteger) + return (BigInteger)value; + if (value is string) + return BigInteger.Parse((string)value, CultureInfo.InvariantCulture); + if (value is float) + return new BigInteger((float)value); + if (value is double) + return new BigInteger((double)value); + if (value is decimal) + return new BigInteger((decimal)value); + if (value is int) + return new BigInteger((int)value); + if (value is long) + return new BigInteger((long)value); + if (value is uint) + return new BigInteger((uint)value); + if (value is ulong) + return new BigInteger((ulong)value); + if (value is byte[]) + return new BigInteger((byte[])value); + + throw new InvalidCastException("Cannot convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + public static object FromBigInteger(BigInteger i, Type targetType) + { + if (targetType == typeof(decimal)) + return (decimal)i; + if (targetType == typeof(double)) + return (double)i; + if (targetType == typeof(float)) + return (float)i; + if (targetType == typeof(ulong)) + return (ulong)i; + + try + { + return System.Convert.ChangeType((long)i, targetType, CultureInfo.InvariantCulture); + } + catch (Exception ex) + { + throw new InvalidOperationException("Can not convert from BigInteger to {0}.".FormatWith(CultureInfo.InvariantCulture, targetType), ex); + } + } +#endif + + #region TryConvert + internal enum ConvertResult + { + Success, + CannotConvertNull, + NotInstantiableType, + NoValidConversion + } + + public static object Convert(object initialValue, CultureInfo culture, Type targetType) + { + object value; + switch (TryConvertInternal(initialValue, culture, targetType, out value)) + { + case ConvertResult.Success: + return value; + case ConvertResult.CannotConvertNull: + throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType)); + case ConvertResult.NotInstantiableType: + throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); + case ConvertResult.NoValidConversion: + throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialValue.GetType(), targetType)); + default: + throw new InvalidOperationException("Unexpected conversion result."); + } + } + + private static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object value) + { + try + { + if (TryConvertInternal(initialValue, culture, targetType, out value) == ConvertResult.Success) + return true; + + value = null; + return false; + } + catch + { + value = null; + return false; + } + } + + private static ConvertResult TryConvertInternal(object initialValue, CultureInfo culture, Type targetType, out object value) + { + if (initialValue == null) + throw new ArgumentNullException("initialValue"); + + if (ReflectionUtils.IsNullableType(targetType)) + targetType = Nullable.GetUnderlyingType(targetType); + + Type initialType = initialValue.GetType(); + + if (targetType == initialType) + { + value = initialValue; + return ConvertResult.Success; + } + + // use Convert.ChangeType if both types are IConvertible + if (ConvertUtils.IsConvertible(initialValue.GetType()) && ConvertUtils.IsConvertible(targetType)) + { + if (targetType.IsEnum()) + { + if (initialValue is string) + { + value = Enum.Parse(targetType, initialValue.ToString(), true); + return ConvertResult.Success; + } + else if (IsInteger(initialValue)) + { + value = Enum.ToObject(targetType, initialValue); + return ConvertResult.Success; + } + } + + value = System.Convert.ChangeType(initialValue, targetType, culture); + return ConvertResult.Success; + } + +#if !NET20 + if (initialValue is DateTime && targetType == typeof(DateTimeOffset)) + { + value = new DateTimeOffset((DateTime)initialValue); + return ConvertResult.Success; + } +#endif + + if (initialValue is byte[] && targetType == typeof(Guid)) + { + value = new Guid((byte[])initialValue); + return ConvertResult.Success; + } + + if (initialValue is Guid && targetType == typeof(byte[])) + { + value = ((Guid)initialValue).ToByteArray(); + return ConvertResult.Success; + } + + if (initialValue is string) + { + if (targetType == typeof(Guid)) + { + value = new Guid((string)initialValue); + return ConvertResult.Success; + } + if (targetType == typeof(Uri)) + { + value = new Uri((string)initialValue, UriKind.RelativeOrAbsolute); + return ConvertResult.Success; + } + if (targetType == typeof(TimeSpan)) + { + value = ParseTimeSpan((string)initialValue); + return ConvertResult.Success; + } + if (targetType == typeof(byte[])) + { + value = System.Convert.FromBase64String((string)initialValue); + return ConvertResult.Success; + } + if (typeof(Type).IsAssignableFrom(targetType)) + { + value = Type.GetType((string)initialValue, true); + return ConvertResult.Success; + } + } + +#if !(NET20 || NET35 || PORTABLE40 || PORTABLE) + if (targetType == typeof(BigInteger)) + { + value = ToBigInteger(initialValue); + return ConvertResult.Success; + } + if (initialValue is BigInteger) + { + value = FromBigInteger((BigInteger)initialValue, targetType); + return ConvertResult.Success; + } +#endif + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // see if source or target types have a TypeConverter that converts between the two + TypeConverter toConverter = GetConverter(initialType); + + if (toConverter != null && toConverter.CanConvertTo(targetType)) + { + value = toConverter.ConvertTo(null, culture, initialValue, targetType); + return ConvertResult.Success; + } + + TypeConverter fromConverter = GetConverter(targetType); + + if (fromConverter != null && fromConverter.CanConvertFrom(initialType)) + { + value = fromConverter.ConvertFrom(null, culture, initialValue); + return ConvertResult.Success; + } +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + // handle DBNull and INullable + if (initialValue == DBNull.Value) + { + if (ReflectionUtils.IsNullable(targetType)) + { + value = EnsureTypeAssignable(null, initialType, targetType); + return ConvertResult.Success; + } + + // cannot convert null to non-nullable + value = null; + return ConvertResult.CannotConvertNull; + } +#endif +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + if (initialValue is INullable) + { + value = EnsureTypeAssignable(ToValue((INullable)initialValue), initialType, targetType); + return ConvertResult.Success; + } +#endif + + if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract()) + { + value = null; + return ConvertResult.NotInstantiableType; + } + + value = null; + return ConvertResult.NoValidConversion; + } + #endregion + + #region ConvertOrCast + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The value to convert. + /// The culture to use when converting. + /// The type to convert or cast the value to. + /// + /// The converted type. If conversion was unsuccessful, the initial value + /// is returned if assignable to the target type. + /// + public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType) + { + object convertedValue; + + if (targetType == typeof(object)) + return initialValue; + + if (initialValue == null && ReflectionUtils.IsNullable(targetType)) + return null; + + if (TryConvert(initialValue, culture, targetType, out convertedValue)) + return convertedValue; + + return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType); + } + #endregion + + private static object EnsureTypeAssignable(object value, Type initialType, Type targetType) + { + Type valueType = (value != null) ? value.GetType() : null; + + if (value != null) + { + if (targetType.IsAssignableFrom(valueType)) + return value; + + Func castConverter = CastConverters.Get(new TypeConvertKey(valueType, targetType)); + if (castConverter != null) + return castConverter(value); + } + else + { + if (ReflectionUtils.IsNullable(targetType)) + return null; + } + + throw new ArgumentException("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType)); + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + public static object ToValue(INullable nullableValue) + { + if (nullableValue == null) + return null; + else if (nullableValue is SqlInt32) + return ToValue((SqlInt32)nullableValue); + else if (nullableValue is SqlInt64) + return ToValue((SqlInt64)nullableValue); + else if (nullableValue is SqlBoolean) + return ToValue((SqlBoolean)nullableValue); + else if (nullableValue is SqlString) + return ToValue((SqlString)nullableValue); + else if (nullableValue is SqlDateTime) + return ToValue((SqlDateTime)nullableValue); + + throw new ArgumentException("Unsupported INullable type: {0}".FormatWith(CultureInfo.InvariantCulture, nullableValue.GetType())); + } +#endif + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + internal static TypeConverter GetConverter(Type t) + { + return JsonTypeReflector.GetTypeConverter(t); + } +#endif + + public static bool IsInteger(object value) + { + switch (GetTypeCode(value.GetType())) + { + case PrimitiveTypeCode.SByte: + case PrimitiveTypeCode.Byte: + case PrimitiveTypeCode.Int16: + case PrimitiveTypeCode.UInt16: + case PrimitiveTypeCode.Int32: + case PrimitiveTypeCode.UInt32: + case PrimitiveTypeCode.Int64: + case PrimitiveTypeCode.UInt64: + return true; + default: + return false; + } + } + + public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value) + { + value = 0; + + if (length == 0) + return ParseResult.Invalid; + + bool isNegative = (chars[start] == '-'); + + if (isNegative) + { + // text just a negative sign + if (length == 1) + return ParseResult.Invalid; + + start++; + length--; + } + + int end = start + length; + + for (int i = start; i < end; i++) + { + int c = chars[i] - '0'; + + if (c < 0 || c > 9) + return ParseResult.Invalid; + + int newValue = (10 * value) - c; + + // overflow has caused the number to loop around + if (newValue > value) + { + i++; + + // double check the rest of the string that there wasn't anything invalid + // invalid result takes precedence over overflow result + for (; i < end; i++) + { + c = chars[i] - '0'; + + if (c < 0 || c > 9) + return ParseResult.Invalid; + } + + return ParseResult.Overflow; + } + + value = newValue; + } + + // go from negative to positive to avoids overflow + // negative can be slightly bigger than positive + if (!isNegative) + { + // negative integer can be one bigger than positive + if (value == int.MinValue) + return ParseResult.Overflow; + + value = -value; + } + + return ParseResult.Success; + } + + public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value) + { + value = 0; + + if (length == 0) + return ParseResult.Invalid; + + bool isNegative = (chars[start] == '-'); + + if (isNegative) + { + // text just a negative sign + if (length == 1) + return ParseResult.Invalid; + + start++; + length--; + } + + int end = start + length; + + for (int i = start; i < end; i++) + { + int c = chars[i] - '0'; + + if (c < 0 || c > 9) + return ParseResult.Invalid; + + long newValue = (10 * value) - c; + + // overflow has caused the number to loop around + if (newValue > value) + { + i++; + + // double check the rest of the string that there wasn't anything invalid + // invalid result takes precedence over overflow result + for (; i < end; i++) + { + c = chars[i] - '0'; + + if (c < 0 || c > 9) + return ParseResult.Invalid; + } + + return ParseResult.Overflow; + } + + value = newValue; + } + + // go from negative to positive to avoids overflow + // negative can be slightly bigger than positive + if (!isNegative) + { + // negative integer can be one bigger than positive + if (value == long.MinValue) + return ParseResult.Overflow; + + value = -value; + } + + return ParseResult.Success; + } + + public static bool TryConvertGuid(string s, out Guid g) + { +#if NET20 || NET35 + if (s == null) + throw new ArgumentNullException("s"); + + Regex format = new Regex( + "^[A-Fa-f0-9]{32}$|" + + "^({|\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\))?$|" + + "^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$"); + Match match = format.Match(s); + if (match.Success) + { + g = new Guid(s); + return true; + } + + g = Guid.Empty; + return false; +#else + return Guid.TryParse(s, out g); +#endif + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DateTimeParser.cs b/Libs/JsonNet/Utilities/DateTimeParser.cs new file mode 100644 index 0000000..3188d56 --- /dev/null +++ b/Libs/JsonNet/Utilities/DateTimeParser.cs @@ -0,0 +1,265 @@ +#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; + +namespace Newtonsoft.Json.Utilities +{ + internal enum ParserTimeZone + { + Unspecified, + Utc, + LocalWestOfUtc, + LocalEastOfUtc + } + + internal struct DateTimeParser + { + static DateTimeParser() + { + Power10 = new[] { -1, 10, 100, 1000, 10000, 100000, 1000000 }; + + Lzyyyy = "yyyy".Length; + Lzyyyy_ = "yyyy-".Length; + Lzyyyy_MM = "yyyy-MM".Length; + Lzyyyy_MM_ = "yyyy-MM-".Length; + Lzyyyy_MM_dd = "yyyy-MM-dd".Length; + Lzyyyy_MM_ddT = "yyyy-MM-ddT".Length; + LzHH = "HH".Length; + LzHH_ = "HH:".Length; + LzHH_mm = "HH:mm".Length; + LzHH_mm_ = "HH:mm:".Length; + LzHH_mm_ss = "HH:mm:ss".Length; + Lz_ = "-".Length; + Lz_zz = "-zz".Length; + } + + public int Year; + public int Month; + public int Day; + public int Hour; + public int Minute; + public int Second; + public int Fraction; + public int ZoneHour; + public int ZoneMinute; + public ParserTimeZone Zone; + + private string _text; + private int _length; + + private static readonly int[] Power10; + + private static readonly int Lzyyyy; + private static readonly int Lzyyyy_; + private static readonly int Lzyyyy_MM; + private static readonly int Lzyyyy_MM_; + private static readonly int Lzyyyy_MM_dd; + private static readonly int Lzyyyy_MM_ddT; + private static readonly int LzHH; + private static readonly int LzHH_; + private static readonly int LzHH_mm; + private static readonly int LzHH_mm_; + private static readonly int LzHH_mm_ss; + private static readonly int Lz_; + private static readonly int Lz_zz; + + private const short MaxFractionDigits = 7; + + public bool Parse(string text) + { + _text = text; + _length = text.Length; + + if (ParseDate(0) && ParseChar(Lzyyyy_MM_dd, 'T') && ParseTimeAndZoneAndWhitespace(Lzyyyy_MM_ddT)) + return true; + + return false; + } + + private bool ParseDate(int start) + { + return (Parse4Digit(start, out Year) + && 1 <= Year + && ParseChar(start + Lzyyyy, '-') + && Parse2Digit(start + Lzyyyy_, out Month) + && 1 <= Month + && Month <= 12 + && ParseChar(start + Lzyyyy_MM, '-') + && Parse2Digit(start + Lzyyyy_MM_, out Day) + && 1 <= Day + && Day <= DateTime.DaysInMonth(Year, Month)); + } + + private bool ParseTimeAndZoneAndWhitespace(int start) + { + return (ParseTime(ref start) && ParseZone(start)); + } + + private bool ParseTime(ref int start) + { + if (!(Parse2Digit(start, out Hour) + && Hour < 24 + && ParseChar(start + LzHH, ':') + && Parse2Digit(start + LzHH_, out Minute) + && Minute < 60 + && ParseChar(start + LzHH_mm, ':') + && Parse2Digit(start + LzHH_mm_, out Second) + && Second < 60)) + { + return false; + } + + start += LzHH_mm_ss; + if (ParseChar(start, '.')) + { + Fraction = 0; + int numberOfDigits = 0; + + while (++start < _length && numberOfDigits < MaxFractionDigits) + { + int digit = _text[start] - '0'; + if (digit < 0 || digit > 9) + break; + + Fraction = (Fraction * 10) + digit; + + numberOfDigits++; + } + + if (numberOfDigits < MaxFractionDigits) + { + if (numberOfDigits == 0) + return false; + + Fraction *= Power10[MaxFractionDigits - numberOfDigits]; + } + } + return true; + } + + private bool ParseZone(int start) + { + if (start < _length) + { + char ch = _text[start]; + if (ch == 'Z' || ch == 'z') + { + Zone = ParserTimeZone.Utc; + start++; + } + else + { + if (start + 2 < _length + && Parse2Digit(start + Lz_, out ZoneHour) + && ZoneHour <= 99) + { + switch (ch) + { + case '-': + Zone = ParserTimeZone.LocalWestOfUtc; + start += Lz_zz; + break; + + case '+': + Zone = ParserTimeZone.LocalEastOfUtc; + start += Lz_zz; + break; + } + } + + if (start < _length) + { + if (ParseChar(start, ':')) + { + start += 1; + + if (start + 1 < _length + && Parse2Digit(start, out ZoneMinute) + && ZoneMinute <= 99) + { + start += 2; + } + } + else + { + if (start + 1 < _length + && Parse2Digit(start, out ZoneMinute) + && ZoneMinute <= 99) + { + start += 2; + } + } + } + } + } + + return (start == _length); + } + + private bool Parse4Digit(int start, out int num) + { + if (start + 3 < _length) + { + int digit1 = _text[start] - '0'; + int digit2 = _text[start + 1] - '0'; + int digit3 = _text[start + 2] - '0'; + int digit4 = _text[start + 3] - '0'; + if (0 <= digit1 && digit1 < 10 + && 0 <= digit2 && digit2 < 10 + && 0 <= digit3 && digit3 < 10 + && 0 <= digit4 && digit4 < 10) + { + num = (((((digit1 * 10) + digit2) * 10) + digit3) * 10) + digit4; + return true; + } + } + num = 0; + return false; + } + + private bool Parse2Digit(int start, out int num) + { + if (start + 1 < _length) + { + int digit1 = _text[start] - '0'; + int digit2 = _text[start + 1] - '0'; + if (0 <= digit1 && digit1 < 10 + && 0 <= digit2 && digit2 < 10) + { + num = (digit1 * 10) + digit2; + return true; + } + } + num = 0; + return false; + } + + private bool ParseChar(int start, char ch) + { + return (start < _length && _text[start] == ch); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DateTimeUtils.cs b/Libs/JsonNet/Utilities/DateTimeUtils.cs new file mode 100644 index 0000000..9f0bb19 --- /dev/null +++ b/Libs/JsonNet/Utilities/DateTimeUtils.cs @@ -0,0 +1,621 @@ +#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.IO; +using System.Xml; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class DateTimeUtils + { + internal static readonly long InitialJavaScriptDateTicks = 621355968000000000; + + private const int DaysPer100Years = 36524; + private const int DaysPer400Years = 146097; + private const int DaysPer4Years = 1461; + private const int DaysPerYear = 365; + private const long TicksPerDay = 864000000000L; + private static readonly int[] DaysToMonth365; + private static readonly int[] DaysToMonth366; + + static DateTimeUtils() + { + DaysToMonth365 = new[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + DaysToMonth366 = new[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; + } + + public static TimeSpan GetUtcOffset(this DateTime d) + { +#if NET20 + return TimeZone.CurrentTimeZone.GetUtcOffset(d); +#else + return TimeZoneInfo.Local.GetUtcOffset(d); +#endif + } + +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + public static XmlDateTimeSerializationMode ToSerializationMode(DateTimeKind kind) + { + switch (kind) + { + case DateTimeKind.Local: + return XmlDateTimeSerializationMode.Local; + case DateTimeKind.Unspecified: + return XmlDateTimeSerializationMode.Unspecified; + case DateTimeKind.Utc: + return XmlDateTimeSerializationMode.Utc; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("kind", kind, "Unexpected DateTimeKind value."); + } + } +#endif + + internal static DateTime EnsureDateTime(DateTime value, DateTimeZoneHandling timeZone) + { + switch (timeZone) + { + case DateTimeZoneHandling.Local: + value = SwitchToLocalTime(value); + break; + case DateTimeZoneHandling.Utc: + value = SwitchToUtcTime(value); + break; + case DateTimeZoneHandling.Unspecified: + value = new DateTime(value.Ticks, DateTimeKind.Unspecified); + break; + case DateTimeZoneHandling.RoundtripKind: + break; + default: + throw new ArgumentException("Invalid date time handling value."); + } + + return value; + } + + private static DateTime SwitchToLocalTime(DateTime value) + { + switch (value.Kind) + { + case DateTimeKind.Unspecified: + return new DateTime(value.Ticks, DateTimeKind.Local); + + case DateTimeKind.Utc: + return value.ToLocalTime(); + + case DateTimeKind.Local: + return value; + } + return value; + } + + private static DateTime SwitchToUtcTime(DateTime value) + { + switch (value.Kind) + { + case DateTimeKind.Unspecified: + return new DateTime(value.Ticks, DateTimeKind.Utc); + + case DateTimeKind.Utc: + return value; + + case DateTimeKind.Local: + return value.ToUniversalTime(); + } + return value; + } + + private static long ToUniversalTicks(DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return dateTime.Ticks; + + return ToUniversalTicks(dateTime, dateTime.GetUtcOffset()); + } + + private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset) + { + // special case min and max value + // they never have a timezone appended to avoid issues + if (dateTime.Kind == DateTimeKind.Utc || dateTime == DateTime.MaxValue || dateTime == DateTime.MinValue) + 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) + { + return ConvertDateTimeToJavaScriptTicks(dateTime, true); + } + + internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, bool convertToUtc) + { + long ticks = (convertToUtc) ? ToUniversalTicks(dateTime) : dateTime.Ticks; + + return UniversialTicksToJavaScriptTicks(ticks); + } + + private static long UniversialTicksToJavaScriptTicks(long universialTicks) + { + long javaScriptTicks = (universialTicks - InitialJavaScriptDateTicks) / 10000; + + return javaScriptTicks; + } + + internal static DateTime ConvertJavaScriptTicksToDateTime(long javaScriptTicks) + { + DateTime dateTime = new DateTime((javaScriptTicks * 10000) + InitialJavaScriptDateTicks, DateTimeKind.Utc); + + return dateTime; + } + + #region Parse + internal static bool TryParseDateIso(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt) + { + DateTimeParser dateTimeParser = new DateTimeParser(); + if (!dateTimeParser.Parse(text)) + { + dt = null; + return false; + } + + DateTime d = new DateTime(dateTimeParser.Year, dateTimeParser.Month, dateTimeParser.Day, dateTimeParser.Hour, dateTimeParser.Minute, dateTimeParser.Second); + d = d.AddTicks(dateTimeParser.Fraction); + +#if !NET20 + if (dateParseHandling == DateParseHandling.DateTimeOffset) + { + TimeSpan offset; + + switch (dateTimeParser.Zone) + { + case ParserTimeZone.Utc: + offset = new TimeSpan(0L); + break; + case ParserTimeZone.LocalWestOfUtc: + offset = new TimeSpan(-dateTimeParser.ZoneHour, -dateTimeParser.ZoneMinute, 0); + break; + case ParserTimeZone.LocalEastOfUtc: + offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0); + break; + default: + offset = TimeZoneInfo.Local.GetUtcOffset(d); + break; + } + + long ticks = d.Ticks - offset.Ticks; + if (ticks < 0 || ticks > 3155378975999999999) + { + dt = null; + return false; + } + + dt = new DateTimeOffset(d, offset); + return true; + } + else +#endif + { + long ticks; + + switch (dateTimeParser.Zone) + { + case ParserTimeZone.Utc: + d = new DateTime(d.Ticks, DateTimeKind.Utc); + break; + + case ParserTimeZone.LocalWestOfUtc: + { + TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0); + ticks = d.Ticks + offset.Ticks; + if (ticks <= DateTime.MaxValue.Ticks) + { + d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime(); + } + else + { + ticks += d.GetUtcOffset().Ticks; + if (ticks > DateTime.MaxValue.Ticks) + ticks = DateTime.MaxValue.Ticks; + + d = new DateTime(ticks, DateTimeKind.Local); + } + break; + } + case ParserTimeZone.LocalEastOfUtc: + { + TimeSpan offset = new TimeSpan(dateTimeParser.ZoneHour, dateTimeParser.ZoneMinute, 0); + ticks = d.Ticks - offset.Ticks; + if (ticks >= DateTime.MinValue.Ticks) + { + d = new DateTime(ticks, DateTimeKind.Utc).ToLocalTime(); + } + else + { + ticks += d.GetUtcOffset().Ticks; + if (ticks < DateTime.MinValue.Ticks) + ticks = DateTime.MinValue.Ticks; + + d = new DateTime(ticks, DateTimeKind.Local); + } + break; + } + } + + dt = EnsureDateTime(d, dateTimeZoneHandling); + return true; + } + } + + internal static bool TryParseDateTime(string s, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out object dt) + { + if (s.Length > 0) + { + if (s[0] == '/') + { + if (s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal)) + { + if (TryParseDateMicrosoft(s, dateParseHandling, dateTimeZoneHandling, out dt)) + return true; + } + } + else if (s.Length >= 19 && s.Length <= 40 && char.IsDigit(s[0]) && s[10] == 'T') + { + if (TryParseDateIso(s, dateParseHandling, dateTimeZoneHandling, out dt)) + return true; + } + + if (!string.IsNullOrEmpty(dateFormatString)) + { + if (TryParseDateExact(s, dateParseHandling, dateTimeZoneHandling, dateFormatString, culture, out dt)) + return true; + } + } + + dt = null; + return false; + } + + private static bool TryParseDateMicrosoft(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt) + { + 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 !NET20 + TimeSpan offset = TimeSpan.Zero; +#endif + + if (index != -1) + { + kind = DateTimeKind.Local; +#if !NET20 + offset = ReadOffset(value.Substring(index)); +#endif + value = value.Substring(0, index); + } + + long javaScriptTicks; + if (!long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out javaScriptTicks)) + { + dt = null; + return false; + } + + DateTime utcDateTime = ConvertJavaScriptTicksToDateTime(javaScriptTicks); + +#if !NET20 + if (dateParseHandling == DateParseHandling.DateTimeOffset) + { + dt = new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset); + return true; + } +#endif + + 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; + } + + dt = EnsureDateTime(dateTime, dateTimeZoneHandling); + return true; + } + + private static bool TryParseDateExact(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, string dateFormatString, CultureInfo culture, out object dt) + { +#if !NET20 + if (dateParseHandling == DateParseHandling.DateTimeOffset) + { + DateTimeOffset temp; + if (DateTimeOffset.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp)) + { + dt = temp; + return true; + } + } + else +#endif + { + DateTime temp; + if (DateTime.TryParseExact(text, dateFormatString, culture, DateTimeStyles.RoundtripKind, out temp)) + { + temp = EnsureDateTime(temp, dateTimeZoneHandling); + dt = temp; + return true; + } + } + + dt = null; + return false; + } + +#if !NET20 + private static TimeSpan ReadOffset(string offsetText) + { + bool negative = (offsetText[0] == '-'); + + int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + int minutes = 0; + if (offsetText.Length >= 5) + minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + + TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes); + if (negative) + offset = offset.Negate(); + + return offset; + } +#endif + #endregion + + #region Write + internal static void WriteDateTimeString(TextWriter writer, DateTime value, DateFormatHandling format, string formatString, CultureInfo culture) + { + if (string.IsNullOrEmpty(formatString)) + { + char[] chars = new char[64]; + int pos = WriteDateTimeString(chars, 0, value, null, value.Kind, format); + writer.Write(chars, 0, pos); + } + else + { + writer.Write(value.ToString(formatString, culture)); + } + } + + internal static int WriteDateTimeString(char[] chars, int start, DateTime value, TimeSpan? offset, DateTimeKind kind, DateFormatHandling format) + { + int pos = start; + + if (format == DateFormatHandling.MicrosoftDateFormat) + { + TimeSpan o = offset ?? value.GetUtcOffset(); + + long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value, o); + + @"\/Date(".CopyTo(0, chars, pos, 7); + pos += 7; + + string ticksText = javaScriptTicks.ToString(CultureInfo.InvariantCulture); + ticksText.CopyTo(0, chars, pos, ticksText.Length); + pos += ticksText.Length; + + switch (kind) + { + case DateTimeKind.Unspecified: + if (value != DateTime.MaxValue && value != DateTime.MinValue) + pos = WriteDateTimeOffset(chars, pos, o, format); + break; + case DateTimeKind.Local: + pos = WriteDateTimeOffset(chars, pos, o, format); + break; + } + + @")\/".CopyTo(0, chars, pos, 3); + pos += 3; + } + else + { + pos = WriteDefaultIsoDate(chars, pos, value); + + switch (kind) + { + case DateTimeKind.Local: + pos = WriteDateTimeOffset(chars, pos, offset ?? value.GetUtcOffset(), format); + break; + case DateTimeKind.Utc: + chars[pos++] = 'Z'; + break; + } + } + + return pos; + } + + internal static int WriteDefaultIsoDate(char[] chars, int start, DateTime dt) + { + int length = 19; + + int year; + int month; + int day; + GetDateValues(dt, out year, out month, out day); + + CopyIntToCharArray(chars, start, year, 4); + chars[start + 4] = '-'; + CopyIntToCharArray(chars, start + 5, month, 2); + chars[start + 7] = '-'; + CopyIntToCharArray(chars, start + 8, day, 2); + chars[start + 10] = 'T'; + CopyIntToCharArray(chars, start + 11, dt.Hour, 2); + chars[start + 13] = ':'; + CopyIntToCharArray(chars, start + 14, dt.Minute, 2); + chars[start + 16] = ':'; + CopyIntToCharArray(chars, start + 17, dt.Second, 2); + + int fraction = (int)(dt.Ticks % 10000000L); + + if (fraction != 0) + { + int digits = 7; + while ((fraction % 10) == 0) + { + digits--; + fraction /= 10; + } + + chars[start + 19] = '.'; + CopyIntToCharArray(chars, start + 20, fraction, digits); + + length += digits + 1; + } + + return start + length; + } + + private static void CopyIntToCharArray(char[] chars, int start, int value, int digits) + { + while (digits-- != 0) + { + chars[start + digits] = (char)((value % 10) + 48); + value /= 10; + } + } + + internal static int WriteDateTimeOffset(char[] chars, int start, TimeSpan offset, DateFormatHandling format) + { + chars[start++] = (offset.Ticks >= 0L) ? '+' : '-'; + + int absHours = Math.Abs(offset.Hours); + CopyIntToCharArray(chars, start, absHours, 2); + start += 2; + + if (format == DateFormatHandling.IsoDateFormat) + chars[start++] = ':'; + + int absMinutes = Math.Abs(offset.Minutes); + CopyIntToCharArray(chars, start, absMinutes, 2); + start += 2; + + return start; + } + +#if !NET20 + internal static void WriteDateTimeOffsetString(TextWriter writer, DateTimeOffset value, DateFormatHandling format, string formatString, CultureInfo culture) + { + if (string.IsNullOrEmpty(formatString)) + { + char[] chars = new char[64]; + int pos = WriteDateTimeString(chars, 0, (format == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, format); + + writer.Write(chars, 0, pos); + } + else + { + writer.Write(value.ToString(formatString, culture)); + } + } +#endif + #endregion + + private static void GetDateValues(DateTime td, out int year, out int month, out int day) + { + long ticks = td.Ticks; + // n = number of days since 1/1/0001 + int n = (int)(ticks / TicksPerDay); + // y400 = number of whole 400-year periods since 1/1/0001 + int y400 = n / DaysPer400Years; + // n = day number within 400-year period + n -= y400 * DaysPer400Years; + // y100 = number of whole 100-year periods within 400-year period + int y100 = n / DaysPer100Years; + // Last 100-year period has an extra day, so decrement result if 4 + if (y100 == 4) + y100 = 3; + // n = day number within 100-year period + n -= y100 * DaysPer100Years; + // y4 = number of whole 4-year periods within 100-year period + int y4 = n / DaysPer4Years; + // n = day number within 4-year period + n -= y4 * DaysPer4Years; + // y1 = number of whole years within 4-year period + int y1 = n / DaysPerYear; + // Last year has an extra day, so decrement result if 4 + if (y1 == 4) + y1 = 3; + + year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; + + // n = day number within year + n -= y1 * DaysPerYear; + + // Leap year calculation looks different from IsLeapYear since y1, y4, + // and y100 are relative to year 1, not year 0 + bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); + int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; + // All months have less than 32 days, so n >> 5 is a good conservative + // estimate for the month + int m = n >> 5 + 1; + // m = 1-based month number + while (n >= days[m]) + { + m++; + } + + month = m; + + // Return 1-based day-of-month + day = n - days[m - 1] + 1; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DictionaryWrapper.cs b/Libs/JsonNet/Utilities/DictionaryWrapper.cs new file mode 100644 index 0000000..457d40e --- /dev/null +++ b/Libs/JsonNet/Utilities/DictionaryWrapper.cs @@ -0,0 +1,556 @@ +#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.Collections; +using System.Threading; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal interface IWrappedDictionary + : IDictionary + { + object UnderlyingDictionary { get; } + } + + internal class DictionaryWrapper : IDictionary, IWrappedDictionary + { + private readonly IDictionary _dictionary; + private readonly IDictionary _genericDictionary; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + private readonly IReadOnlyDictionary _readOnlyDictionary; +#endif + private object _syncRoot; + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _dictionary = dictionary; + } + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _genericDictionary = dictionary; + } + +#if !(NET40 || NET35 || NET20 || PORTABLE40) + public DictionaryWrapper(IReadOnlyDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _readOnlyDictionary = dictionary; + } +#endif + + public void Add(TKey key, TValue value) + { + if (_dictionary != null) + _dictionary.Add(key, value); + else if (_genericDictionary != null) + _genericDictionary.Add(key, value); + else + throw new NotSupportedException(); + } + + public bool ContainsKey(TKey key) + { + if (_dictionary != null) + return _dictionary.Contains(key); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.ContainsKey(key); +#endif + else + return _genericDictionary.ContainsKey(key); + } + + public ICollection Keys + { + get + { + if (_dictionary != null) + return _dictionary.Keys.Cast().ToList(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Keys.ToList(); +#endif + else + return _genericDictionary.Keys; + } + } + + public bool Remove(TKey key) + { + if (_dictionary != null) + { + if (_dictionary.Contains(key)) + { + _dictionary.Remove(key); + return true; + } + else + { + return false; + } + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + { + throw new NotSupportedException(); + } +#endif + else + { + return _genericDictionary.Remove(key); + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + if (_dictionary != null) + { + if (!_dictionary.Contains(key)) + { + value = default(TValue); + return false; + } + else + { + value = (TValue)_dictionary[key]; + return true; + } + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + { + throw new NotSupportedException(); + } +#endif + else + { + return _genericDictionary.TryGetValue(key, out value); + } + } + + public ICollection Values + { + get + { + if (_dictionary != null) + return _dictionary.Values.Cast().ToList(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Values.ToList(); +#endif + else + return _genericDictionary.Values; + } + } + + public TValue this[TKey key] + { + get + { + if (_dictionary != null) + return (TValue)_dictionary[key]; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary[key]; +#endif + else + return _genericDictionary[key]; + } + set + { + if (_dictionary != null) + _dictionary[key] = value; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary[key] = value; + } + } + + public void Add(KeyValuePair item) + { + if (_dictionary != null) + ((IList)_dictionary).Add(item); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else if (_genericDictionary != null) + _genericDictionary.Add(item); + } + + public void Clear() + { + if (_dictionary != null) + _dictionary.Clear(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + if (_dictionary != null) + return ((IList)_dictionary).Contains(item); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Contains(item); +#endif + else + return _genericDictionary.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_dictionary != null) + { + foreach (DictionaryEntry item in _dictionary) + { + array[arrayIndex++] = new KeyValuePair((TKey)item.Key, (TValue)item.Value); + } + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + { + throw new NotSupportedException(); + } +#endif + else + { + _genericDictionary.CopyTo(array, arrayIndex); + } + } + + public int Count + { + get + { + if (_dictionary != null) + return _dictionary.Count; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Count; +#endif + else + return _genericDictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + if (_dictionary != null) + return _dictionary.IsReadOnly; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return true; +#endif + else + return _genericDictionary.IsReadOnly; + } + } + + public bool Remove(KeyValuePair item) + { + if (_dictionary != null) + { + if (_dictionary.Contains(item.Key)) + { + object value = _dictionary[item.Key]; + + if (object.Equals(value, item.Value)) + { + _dictionary.Remove(item.Key); + return true; + } + else + { + return false; + } + } + else + { + return true; + } + } +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + { + throw new NotSupportedException(); + } +#endif + else + { + return _genericDictionary.Remove(item); + } + } + + public IEnumerator> GetEnumerator() + { + if (_dictionary != null) + return _dictionary.Cast().Select(de => new KeyValuePair((TKey)de.Key, (TValue)de.Value)).GetEnumerator(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.GetEnumerator(); +#endif + else + return _genericDictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + void IDictionary.Add(object key, object value) + { + if (_dictionary != null) + _dictionary.Add(key, value); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary.Add((TKey)key, (TValue)value); + } + + object IDictionary.this[object key] + { + get + { + if (_dictionary != null) + return _dictionary[key]; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary[(TKey)key]; +#endif + else + return _genericDictionary[(TKey)key]; + } + set + { + if (_dictionary != null) + _dictionary[key] = value; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary[(TKey)key] = (TValue)value; + } + } + + private struct DictionaryEnumerator : IDictionaryEnumerator + { + private readonly IEnumerator> _e; + + public DictionaryEnumerator(IEnumerator> e) + { + ValidationUtils.ArgumentNotNull(e, "e"); + _e = e; + } + + public DictionaryEntry Entry + { + get { return (DictionaryEntry)Current; } + } + + public object Key + { + get { return Entry.Key; } + } + + public object Value + { + get { return Entry.Value; } + } + + public object Current + { + get { return new DictionaryEntry(_e.Current.Key, _e.Current.Value); } + } + + public bool MoveNext() + { + return _e.MoveNext(); + } + + public void Reset() + { + _e.Reset(); + } + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + if (_dictionary != null) + return _dictionary.GetEnumerator(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return new DictionaryEnumerator(_readOnlyDictionary.GetEnumerator()); +#endif + else + return new DictionaryEnumerator(_genericDictionary.GetEnumerator()); + } + + bool IDictionary.Contains(object key) + { + if (_genericDictionary != null) + return _genericDictionary.ContainsKey((TKey)key); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.ContainsKey((TKey)key); +#endif + else + return _dictionary.Contains(key); + } + + bool IDictionary.IsFixedSize + { + get + { + if (_genericDictionary != null) + return false; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return true; +#endif + else + return _dictionary.IsFixedSize; + } + } + + ICollection IDictionary.Keys + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Keys.ToList(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Keys.ToList(); +#endif + else + return _dictionary.Keys; + } + } + + public void Remove(object key) + { + if (_dictionary != null) + _dictionary.Remove(key); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary.Remove((TKey)key); + } + + ICollection IDictionary.Values + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Values.ToList(); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary.Values.ToList(); +#endif + else + return _dictionary.Values; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (_dictionary != null) + _dictionary.CopyTo(array, index); +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + throw new NotSupportedException(); +#endif + else + _genericDictionary.CopyTo((KeyValuePair[])array, index); + } + + bool ICollection.IsSynchronized + { + get + { + if (_dictionary != null) + return _dictionary.IsSynchronized; + else + return false; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + + public object UnderlyingDictionary + { + get + { + if (_dictionary != null) + return _dictionary; +#if !(NET40 || NET35 || NET20 || PORTABLE40) + else if (_readOnlyDictionary != null) + return _readOnlyDictionary; +#endif + else + return _genericDictionary; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DynamicProxy.cs b/Libs/JsonNet/Utilities/DynamicProxy.cs new file mode 100644 index 0000000..7fb0100 --- /dev/null +++ b/Libs/JsonNet/Utilities/DynamicProxy.cs @@ -0,0 +1,113 @@ +#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 !(NET35 || NET20 || PORTABLE40) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicProxy + { + public virtual IEnumerable GetDynamicMemberNames(T instance) + { + return new string[0]; + } + + public virtual bool TryBinaryOperation(T instance, BinaryOperationBinder binder, object arg, out object result) + { + result = null; + return false; + } + + public virtual bool TryConvert(T instance, ConvertBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryCreateInstance(T instance, CreateInstanceBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryDeleteIndex(T instance, DeleteIndexBinder binder, object[] indexes) + { + return false; + } + + public virtual bool TryDeleteMember(T instance, DeleteMemberBinder binder) + { + return false; + } + + public virtual bool TryGetIndex(T instance, GetIndexBinder binder, object[] indexes, out object result) + { + result = null; + return false; + } + + public virtual bool TryGetMember(T instance, GetMemberBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvoke(T instance, InvokeBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvokeMember(T instance, InvokeMemberBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TrySetIndex(T instance, SetIndexBinder binder, object[] indexes, object value) + { + return false; + } + + public virtual bool TrySetMember(T instance, SetMemberBinder binder, object value) + { + return false; + } + + public virtual bool TryUnaryOperation(T instance, UnaryOperationBinder binder, out object result) + { + result = null; + return false; + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DynamicProxyMetaObject.cs b/Libs/JsonNet/Utilities/DynamicProxyMetaObject.cs new file mode 100644 index 0000000..b326899 --- /dev/null +++ b/Libs/JsonNet/Utilities/DynamicProxyMetaObject.cs @@ -0,0 +1,423 @@ +#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 !(NET35 || NET20 || PORTABLE40) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal sealed class DynamicProxyMetaObject : DynamicMetaObject + { + private readonly DynamicProxy _proxy; + private readonly bool _dontFallbackFirst; + + internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy proxy, bool dontFallbackFirst) + : base(expression, BindingRestrictions.Empty, value) + { + _proxy = proxy; + _dontFallbackFirst = dontFallbackFirst; + } + + private new T Value + { + get { return (T)base.Value; } + } + + private bool IsOverridden(string method) + { + return ReflectionUtils.IsMethodOverridden(_proxy.GetType(), typeof(DynamicProxy), method); + } + + public override DynamicMetaObject BindGetMember(GetMemberBinder binder) + { + return IsOverridden("TryGetMember") + ? CallMethodWithResult("TryGetMember", binder, NoArgs, e => binder.FallbackGetMember(this, e)) + : base.BindGetMember(binder); + } + + public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) + { + return IsOverridden("TrySetMember") + ? CallMethodReturnLast("TrySetMember", binder, GetArgs(value), e => binder.FallbackSetMember(this, value, e)) + : base.BindSetMember(binder, value); + } + + public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) + { + return IsOverridden("TryDeleteMember") + ? CallMethodNoResult("TryDeleteMember", binder, NoArgs, e => binder.FallbackDeleteMember(this, e)) + : base.BindDeleteMember(binder); + } + + + public override DynamicMetaObject BindConvert(ConvertBinder binder) + { + return IsOverridden("TryConvert") + ? CallMethodWithResult("TryConvert", binder, NoArgs, e => binder.FallbackConvert(this, e)) + : base.BindConvert(binder); + } + + public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) + { + if (!IsOverridden("TryInvokeMember")) + return base.BindInvokeMember(binder, args); + + // + // Generate a tree like: + // + // { + // object result; + // TryInvokeMember(payload, out result) + // ? result + // : TryGetMember(payload, out result) + // ? FallbackInvoke(result) + // : fallbackResult + // } + // + // Then it calls FallbackInvokeMember with this tree as the + // "error", giving the language the option of using this + // tree or doing .NET binding. + // + Fallback fallback = e => binder.FallbackInvokeMember(this, args, e); + + DynamicMetaObject call = BuildCallMethodWithResult( + "TryInvokeMember", + binder, + GetArgArray(args), + BuildCallMethodWithResult( + "TryGetMember", + new GetBinderAdapter(binder), + NoArgs, + fallback(null), + e => binder.FallbackInvoke(e, args, null) + ), + null + ); + + return _dontFallbackFirst ? call : fallback(call); + } + + + public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryCreateInstance") + ? CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), e => binder.FallbackCreateInstance(this, args, e)) + : base.BindCreateInstance(binder, args); + } + + public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryInvoke") + ? CallMethodWithResult("TryInvoke", binder, GetArgArray(args), e => binder.FallbackInvoke(this, args, e)) + : base.BindInvoke(binder, args); + } + + public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) + { + return IsOverridden("TryBinaryOperation") + ? CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), e => binder.FallbackBinaryOperation(this, arg, e)) + : base.BindBinaryOperation(binder, arg); + } + + public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) + { + return IsOverridden("TryUnaryOperation") + ? CallMethodWithResult("TryUnaryOperation", binder, NoArgs, e => binder.FallbackUnaryOperation(this, e)) + : base.BindUnaryOperation(binder); + } + + public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryGetIndex") + ? CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), e => binder.FallbackGetIndex(this, indexes, e)) + : base.BindGetIndex(binder, indexes); + } + + public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) + { + return IsOverridden("TrySetIndex") + ? CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), e => binder.FallbackSetIndex(this, indexes, value, e)) + : base.BindSetIndex(binder, indexes, value); + } + + public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryDeleteIndex") + ? CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), e => binder.FallbackDeleteIndex(this, indexes, e)) + : base.BindDeleteIndex(binder, indexes); + } + + private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion); + + private static readonly Expression[] NoArgs = new Expression[0]; + + private static Expression[] GetArgs(params DynamicMetaObject[] args) + { + return args.Select(arg => Expression.Convert(arg.Expression, typeof(object))).ToArray(); + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args) + { + return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) }; + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) + { + return new Expression[] + { + Expression.NewArrayInit(typeof(object), GetArgs(args)), + Expression.Convert(value.Expression, typeof(object)) + }; + } + + private static ConstantExpression Constant(DynamicMetaObjectBinder binder) + { + Type t = binder.GetType(); + while (!t.IsVisible()) + t = t.BaseType(); + return Expression.Constant(binder, t); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic that returns a result + /// + private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke = null) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + DynamicMetaObject callDynamic = BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) + { + // + // Build a new expression like: + // { + // object result; + // TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs.Add(result); + + DynamicMetaObject resultMetaObject = new DynamicMetaObject(result, BindingRestrictions.Empty); + + // Need to add a conversion if calling TryConvert + if (binder.ReturnType != typeof(object)) + { + UnaryExpression convert = Expression.Convert(resultMetaObject.Expression, binder.ReturnType); + // will always be a cast or unbox + + resultMetaObject = new DynamicMetaObject(convert, resultMetaObject.Restrictions); + } + + if (fallbackInvoke != null) + resultMetaObject = fallbackInvoke(resultMetaObject); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + resultMetaObject.Expression, + fallbackResult.Expression, + binder.ReturnType + ) + ), + GetRestrictions().Merge(resultMetaObject.Restrictions).Merge(fallbackResult.Restrictions) + ); + + return callDynamic; + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // { + // object result; + // TrySetMember(payload, result = value) ? result : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs[args.Length + 1] = Expression.Assign(result, callArgs[args.Length + 1]); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + result, + fallbackResult.Expression, + typeof(object) + ) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + + // + // Build a new expression like: + // if (TryDeleteMember(payload)) { } else { fallbackResult } + // + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + Expression.Empty(), + fallbackResult.Expression, + typeof(void) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + /// + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// + private BindingRestrictions GetRestrictions() + { + return (Value == null && HasValue) + ? BindingRestrictions.GetInstanceRestriction(Expression, null) + : BindingRestrictions.GetTypeRestriction(Expression, LimitType); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _proxy.GetDynamicMemberNames(Value); + } + + // It is okay to throw NotSupported from this binder. This object + // is only used by DynamicObject.GetMember--it is not expected to + // (and cannot) implement binding semantics. It is just so the DO + // can use the Name and IgnoreCase properties. + private sealed class GetBinderAdapter : GetMemberBinder + { + internal GetBinderAdapter(InvokeMemberBinder binder) : + base(binder.Name, binder.IgnoreCase) + { + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + throw new NotSupportedException(); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DynamicReflectionDelegateFactory.cs b/Libs/JsonNet/Utilities/DynamicReflectionDelegateFactory.cs new file mode 100644 index 0000000..6710c14 --- /dev/null +++ b/Libs/JsonNet/Utilities/DynamicReflectionDelegateFactory.cs @@ -0,0 +1,328 @@ +#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 !(PORTABLE || NETFX_CORE || PORTABLE40) +using System; +using System.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +using System.Reflection; +using System.Reflection.Emit; +using Newtonsoft.Json.Serialization; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicReflectionDelegateFactory : ReflectionDelegateFactory + { + public static DynamicReflectionDelegateFactory Instance = new DynamicReflectionDelegateFactory(); + + private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner) + { + DynamicMethod dynamicMethod = !owner.IsInterface() + ? new DynamicMethod(name, returnType, parameterTypes, owner, true) + : new DynamicMethod(name, returnType, parameterTypes, owner.Module, true); + + return dynamicMethod; + } + + public override ObjectConstructor CreateParametrizedConstructor(MethodBase method) + { + DynamicMethod dynamicMethod = CreateDynamicMethod(method.ToString(), typeof(object), new[] { typeof(object[]) }, method.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateMethodCallIL(method, generator, 0); + + return (ObjectConstructor)dynamicMethod.CreateDelegate(typeof(ObjectConstructor)); + } + + public override MethodCall CreateMethodCall(MethodBase method) + { + DynamicMethod dynamicMethod = CreateDynamicMethod(method.ToString(), typeof(object), new[] { typeof(object), typeof(object[]) }, method.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateMethodCallIL(method, generator, 1); + + return (MethodCall)dynamicMethod.CreateDelegate(typeof(MethodCall)); + } + + private void GenerateCreateMethodCallIL(MethodBase method, ILGenerator generator, int argsIndex) + { + ParameterInfo[] args = method.GetParameters(); + + Label argsOk = generator.DefineLabel(); + + // throw an error if the number of argument values doesn't match method parameters + generator.Emit(OpCodes.Ldarg, argsIndex); + generator.Emit(OpCodes.Ldlen); + generator.Emit(OpCodes.Ldc_I4, args.Length); + generator.Emit(OpCodes.Beq, argsOk); + generator.Emit(OpCodes.Newobj, typeof(TargetParameterCountException).GetConstructor(ReflectionUtils.EmptyTypes)); + generator.Emit(OpCodes.Throw); + + generator.MarkLabel(argsOk); + + if (!method.IsConstructor && !method.IsStatic) + generator.PushInstance(method.DeclaringType); + + int localVariableCount = 0; + + for (int i = 0; i < args.Length; i++) + { + ParameterInfo parameter = args[i]; + Type parameterType = parameter.ParameterType; + + if (parameterType.IsByRef) + { + parameterType = parameterType.GetElementType(); + + LocalBuilder localVariable = generator.DeclareLocal(parameterType); + + // don't need to set variable for 'out' parameter + if (!parameter.IsOut) + { + generator.PushArrayInstance(argsIndex, i); + + if (parameterType.IsValueType()) + { + Label skipSettingDefault = generator.DefineLabel(); + Label finishedProcessingParameter = generator.DefineLabel(); + + // check if parameter is not null + generator.Emit(OpCodes.Brtrue_S, skipSettingDefault); + + // parameter has no value, initialize to default + generator.Emit(OpCodes.Ldloca_S, localVariable); + generator.Emit(OpCodes.Initobj, parameterType); + generator.Emit(OpCodes.Br_S, finishedProcessingParameter); + + // parameter has value, get value from array again and unbox and set to variable + generator.MarkLabel(skipSettingDefault); + generator.PushArrayInstance(argsIndex, i); + generator.UnboxIfNeeded(parameterType); + generator.Emit(OpCodes.Stloc, localVariableCount); + + // parameter finished, we out! + generator.MarkLabel(finishedProcessingParameter); + } + else + { + generator.UnboxIfNeeded(parameterType); + generator.Emit(OpCodes.Stloc, localVariableCount); + } + } + + generator.Emit(OpCodes.Ldloca_S, localVariable); + + localVariableCount++; + } + else if (parameterType.IsValueType()) + { + generator.PushArrayInstance(argsIndex, i); + + // have to check that value type parameters aren't null + // otherwise they will error when unboxed + Label skipSettingDefault = generator.DefineLabel(); + Label finishedProcessingParameter = generator.DefineLabel(); + + // check if parameter is not null + generator.Emit(OpCodes.Brtrue_S, skipSettingDefault); + + // parameter has no value, initialize to default + LocalBuilder localVariable = generator.DeclareLocal(parameterType); + generator.Emit(OpCodes.Ldloca_S, localVariable); + generator.Emit(OpCodes.Initobj, parameterType); + generator.Emit(OpCodes.Ldloc, localVariableCount); + generator.Emit(OpCodes.Br_S, finishedProcessingParameter); + + // parameter has value, get value from array again and unbox + generator.MarkLabel(skipSettingDefault); + generator.PushArrayInstance(argsIndex, i); + generator.UnboxIfNeeded(parameterType); + + // parameter finished, we out! + generator.MarkLabel(finishedProcessingParameter); + localVariableCount++; + } + else + { + generator.PushArrayInstance(argsIndex, i); + + generator.UnboxIfNeeded(parameterType); + } + } + + if (method.IsConstructor) + generator.Emit(OpCodes.Newobj, (ConstructorInfo)method); + else + generator.CallMethod((MethodInfo)method); + + Type returnType = method.IsConstructor + ? method.DeclaringType + : ((MethodInfo)method).ReturnType; + + if (returnType != typeof(void)) + generator.BoxIfNeeded(returnType); + else + generator.Emit(OpCodes.Ldnull); + + generator.Return(); + } + + public override Func CreateDefaultConstructor(Type type) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Create" + type.FullName, typeof(T), ReflectionUtils.EmptyTypes, type); + dynamicMethod.InitLocals = true; + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateDefaultConstructorIL(type, generator); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + private void GenerateCreateDefaultConstructorIL(Type type, ILGenerator generator) + { + if (type.IsValueType()) + { + generator.DeclareLocal(type); + generator.Emit(OpCodes.Ldloc_0); + generator.Emit(OpCodes.Box, type); + } + else + { + ConstructorInfo constructorInfo = + type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, + ReflectionUtils.EmptyTypes, null); + + if (constructorInfo == null) + throw new ArgumentException("Could not get constructor for {0}.".FormatWith(CultureInfo.InvariantCulture, type)); + + generator.Emit(OpCodes.Newobj, constructorInfo); + } + + generator.Return(); + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + propertyInfo.Name, typeof(T), new[] { typeof(object) }, propertyInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateGetPropertyIL(propertyInfo, generator); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + private void GenerateCreateGetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator) + { + MethodInfo getMethod = propertyInfo.GetGetMethod(true); + if (getMethod == null) + throw new ArgumentException("Property '{0}' does not have a getter.".FormatWith(CultureInfo.InvariantCulture, propertyInfo.Name)); + + if (!getMethod.IsStatic) + generator.PushInstance(propertyInfo.DeclaringType); + + generator.CallMethod(getMethod); + generator.BoxIfNeeded(propertyInfo.PropertyType); + generator.Return(); + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + fieldInfo.Name, typeof(T), new[] { typeof(object) }, fieldInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateGetFieldIL(fieldInfo, generator); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + private void GenerateCreateGetFieldIL(FieldInfo fieldInfo, ILGenerator generator) + { + if (!fieldInfo.IsStatic) + { + generator.PushInstance(fieldInfo.DeclaringType); + generator.Emit(OpCodes.Ldfld, fieldInfo); + } + else + { + generator.Emit(OpCodes.Ldsfld, fieldInfo); + } + + generator.BoxIfNeeded(fieldInfo.FieldType); + generator.Return(); + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + fieldInfo.Name, null, new[] { typeof(T), typeof(object) }, fieldInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateSetFieldIL(fieldInfo, generator); + + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + + internal static void GenerateCreateSetFieldIL(FieldInfo fieldInfo, ILGenerator generator) + { + if (!fieldInfo.IsStatic) + generator.PushInstance(fieldInfo.DeclaringType); + + generator.Emit(OpCodes.Ldarg_1); + generator.UnboxIfNeeded(fieldInfo.FieldType); + + if (!fieldInfo.IsStatic) + generator.Emit(OpCodes.Stfld, fieldInfo); + else + generator.Emit(OpCodes.Stsfld, fieldInfo); + + generator.Return(); + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + propertyInfo.Name, null, new[] { typeof(T), typeof(object) }, propertyInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + GenerateCreateSetPropertyIL(propertyInfo, generator); + + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + + internal static void GenerateCreateSetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator) + { + MethodInfo setMethod = propertyInfo.GetSetMethod(true); + if (!setMethod.IsStatic) + generator.PushInstance(propertyInfo.DeclaringType); + + generator.Emit(OpCodes.Ldarg_1); + generator.UnboxIfNeeded(propertyInfo.PropertyType); + generator.CallMethod(setMethod); + generator.Return(); + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/DynamicUtils.cs b/Libs/JsonNet/Utilities/DynamicUtils.cs new file mode 100644 index 0000000..a40bd61 --- /dev/null +++ b/Libs/JsonNet/Utilities/DynamicUtils.cs @@ -0,0 +1,206 @@ +#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 !(NET35 || NET20 || PORTABLE40) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +#if !(PORTABLE) +using System.Reflection; +#else +using Microsoft.CSharp.RuntimeBinder; +#endif +using System.Runtime.CompilerServices; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class DynamicUtils + { + internal static class BinderWrapper + { +#if !(PORTABLE) + public const string CSharpAssemblyName = "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + + private const string BinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder, " + CSharpAssemblyName; + private const string CSharpArgumentInfoTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo, " + CSharpAssemblyName; + private const string CSharpArgumentInfoFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, " + CSharpAssemblyName; + private const string CSharpBinderFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, " + CSharpAssemblyName; + + private static object _getCSharpArgumentInfoArray; + private static object _setCSharpArgumentInfoArray; + private static MethodCall _getMemberCall; + private static MethodCall _setMemberCall; + private static bool _init; + + private static void Init() + { + if (!_init) + { + Type binderType = Type.GetType(BinderTypeName, false); + if (binderType == null) + throw new InvalidOperationException("Could not resolve type '{0}'. You may need to add a reference to Microsoft.CSharp.dll to work with dynamic types.".FormatWith(CultureInfo.InvariantCulture, BinderTypeName)); + + // None + _getCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0); + // None, Constant | UseCompileTimeType + _setCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0, 3); + CreateMemberCalls(); + + _init = true; + } + } + + private static object CreateSharpArgumentInfoArray(params int[] values) + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); + Type csharpArgumentInfoFlags = Type.GetType(CSharpArgumentInfoFlagsTypeName); + + Array a = Array.CreateInstance(csharpArgumentInfoType, values.Length); + + for (int i = 0; i < values.Length; i++) + { + MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", new[] { csharpArgumentInfoFlags, typeof(string) }); + object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null }); + a.SetValue(arg, i); + } + + return a; + } + + private static void CreateMemberCalls() + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName, true); + Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName, true); + Type binderType = Type.GetType(BinderTypeName, true); + + Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType); + + MethodInfo getMemberMethod = binderType.GetMethod("GetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }); + _getMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(getMemberMethod); + + MethodInfo setMemberMethod = binderType.GetMethod("SetMember", new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }); + _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(setMemberMethod); + } +#endif + + public static CallSiteBinder GetMember(string name, Type context) + { +#if !(PORTABLE) + Init(); + return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); +#else + return Binder.GetMember( + CSharpBinderFlags.None, name, context, new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}); +#endif + } + + public static CallSiteBinder SetMember(string name, Type context) + { +#if !(PORTABLE) + Init(); + return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); +#else + return Binder.SetMember( + CSharpBinderFlags.None, name, context, new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) + }); +#endif + } + } + + public static IEnumerable GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider) + { + DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider)); + return metaObject.GetDynamicMemberNames(); + } + } + + internal class NoThrowGetBinderMember : GetMemberBinder + { + private readonly GetMemberBinder _innerBinder; + + public NoThrowGetBinderMember(GetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + internal class NoThrowSetBinderMember : SetMemberBinder + { + private readonly SetMemberBinder _innerBinder; + + public NoThrowSetBinderMember(SetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + internal class NoThrowExpressionVisitor : ExpressionVisitor + { + internal static readonly object ErrorResult = new object(); + + protected override Expression VisitConditional(ConditionalExpression node) + { + // if the result of a test is to throw an error, rewrite to result an error result value + if (node.IfFalse.NodeType == ExpressionType.Throw) + return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult)); + + return base.VisitConditional(node); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/EnumUtils.cs b/Libs/JsonNet/Utilities/EnumUtils.cs new file mode 100644 index 0000000..09b168e --- /dev/null +++ b/Libs/JsonNet/Utilities/EnumUtils.cs @@ -0,0 +1,236 @@ +#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.Globalization; +using System.Runtime.Serialization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal static class EnumUtils + { + private static readonly ThreadSafeStore> EnumMemberNamesPerType = new ThreadSafeStore>(InitializeEnumType); + + private static BidirectionalDictionary InitializeEnumType(Type type) + { + BidirectionalDictionary map = new BidirectionalDictionary( + StringComparer.OrdinalIgnoreCase, + StringComparer.OrdinalIgnoreCase); + + foreach (FieldInfo f in type.GetFields()) + { + string n1 = f.Name; + string n2; + +#if !NET20 + n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true) + .Cast() + .Select(a => a.Value) + .SingleOrDefault() ?? f.Name; +#else + n2 = f.Name; +#endif + + string s; + if (map.TryGetBySecond(n2, out s)) + throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, n2, type.Name)); + + map.Set(n1, n2); + } + + return map; + } + + public static IList GetFlagsValues(T value) where T : struct + { + Type enumType = typeof(T); + + if (!enumType.IsDefined(typeof(FlagsAttribute), false)) + throw new ArgumentException("Enum type {0} is not a set of flags.".FormatWith(CultureInfo.InvariantCulture, enumType)); + + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + + ulong num = Convert.ToUInt64(value, CultureInfo.InvariantCulture); + IList> enumNameValues = GetNamesAndValues(); + IList selectedFlagsValues = new List(); + + foreach (EnumValue enumNameValue in enumNameValues) + { + if ((num & enumNameValue.Value) == enumNameValue.Value && enumNameValue.Value != 0) + selectedFlagsValues.Add((T)Convert.ChangeType(enumNameValue.Value, underlyingType, CultureInfo.CurrentCulture)); + } + + if (selectedFlagsValues.Count == 0 && enumNameValues.SingleOrDefault(v => v.Value == 0) != null) + selectedFlagsValues.Add(default(T)); + + return selectedFlagsValues; + } + + /// + /// Gets a dictionary of the names and values of an Enum type. + /// + /// + public static IList> GetNamesAndValues() where T : struct + { + return GetNamesAndValues(typeof(T)); + } + + /// + /// Gets a dictionary of the names and values of an Enum type. + /// + /// The enum type to get names and values for. + /// + public static IList> GetNamesAndValues(Type enumType) where TUnderlyingType : struct + { + if (enumType == null) + throw new ArgumentNullException("enumType"); + + ValidationUtils.ArgumentTypeIsEnum(enumType, "enumType"); + + IList enumValues = GetValues(enumType); + IList enumNames = GetNames(enumType); + + IList> nameValues = new List>(); + + for (int i = 0; i < enumValues.Count; i++) + { + try + { + nameValues.Add(new EnumValue(enumNames[i], (TUnderlyingType)Convert.ChangeType(enumValues[i], typeof(TUnderlyingType), CultureInfo.CurrentCulture))); + } + catch (OverflowException e) + { + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "Value from enum with the underlying type of {0} cannot be added to dictionary with a value type of {1}. Value was too large: {2}", + Enum.GetUnderlyingType(enumType), typeof(TUnderlyingType), Convert.ToUInt64(enumValues[i], CultureInfo.InvariantCulture)), e); + } + } + + return nameValues; + } + + public static IList GetValues(Type enumType) + { + if (!enumType.IsEnum()) + throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); + + List values = new List(); + + var fields = enumType.GetFields().Where(f => f.IsLiteral); + + foreach (FieldInfo field in fields) + { + object value = field.GetValue(enumType); + values.Add(value); + } + + return values; + } + + public static IList GetNames(Type enumType) + { + if (!enumType.IsEnum()) + throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); + + List values = new List(); + + var fields = enumType.GetFields().Where(f => f.IsLiteral); + + foreach (FieldInfo field in fields) + { + values.Add(field.Name); + } + + return values; + } + + public static object ParseEnumName(string enumText, bool isNullable, Type t) + { + if (enumText == string.Empty && isNullable) + return null; + + string finalEnumText; + + BidirectionalDictionary map = EnumMemberNamesPerType.Get(t); + if (enumText.IndexOf(',') != -1) + { + string[] names = enumText.Split(','); + for (int i = 0; i < names.Length; i++) + { + string name = names[i].Trim(); + + names[i] = ResolvedEnumName(map, name); + } + + finalEnumText = string.Join(", ", names); + } + else + { + finalEnumText = ResolvedEnumName(map, enumText); + } + + return Enum.Parse(t, finalEnumText, true); + } + + public static string ToEnumName(Type enumType, string enumText, bool camelCaseText) + { + BidirectionalDictionary map = EnumMemberNamesPerType.Get(enumType); + + string[] names = enumText.Split(','); + for (int i = 0; i < names.Length; i++) + { + string name = names[i].Trim(); + + string resolvedEnumName; + map.TryGetByFirst(name, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? name; + + if (camelCaseText) + resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName); + + names[i] = resolvedEnumName; + } + + string finalName = string.Join(", ", names); + + return finalName; + } + + private static string ResolvedEnumName(BidirectionalDictionary map, string enumText) + { + string resolvedEnumName; + map.TryGetBySecond(enumText, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? enumText; + return resolvedEnumName; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/EnumValue.cs b/Libs/JsonNet/Utilities/EnumValue.cs new file mode 100644 index 0000000..c672ead --- /dev/null +++ b/Libs/JsonNet/Utilities/EnumValue.cs @@ -0,0 +1,49 @@ +#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 + +namespace Newtonsoft.Json.Utilities +{ + internal class EnumValue where T : struct + { + private readonly string _name; + private readonly T _value; + + public string Name + { + get { return _name; } + } + + public T Value + { + get { return _value; } + } + + public EnumValue(string name, T value) + { + _name = name; + _value = value; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ExpressionReflectionDelegateFactory.cs b/Libs/JsonNet/Utilities/ExpressionReflectionDelegateFactory.cs new file mode 100644 index 0000000..6cb95a3 --- /dev/null +++ b/Libs/JsonNet/Utilities/ExpressionReflectionDelegateFactory.cs @@ -0,0 +1,354 @@ +#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 !(NET20 || NET35) +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System; +using System.Linq.Expressions; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal class ExpressionReflectionDelegateFactory : ReflectionDelegateFactory + { + private static readonly ExpressionReflectionDelegateFactory _instance = new ExpressionReflectionDelegateFactory(); + + internal static ReflectionDelegateFactory Instance + { + get { return _instance; } + } + + public override ObjectConstructor CreateParametrizedConstructor(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + Type type = typeof(object); + + ParameterExpression argsParameterExpression = Expression.Parameter(typeof(object[]), "args"); + + Expression callExpression = BuildMethodCall(method, type, null, argsParameterExpression); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(ObjectConstructor), callExpression, argsParameterExpression); + + ObjectConstructor compiled = (ObjectConstructor)lambdaExpression.Compile(); + return compiled; + } + + public override MethodCall CreateMethodCall(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + Type type = typeof(object); + + ParameterExpression targetParameterExpression = Expression.Parameter(type, "target"); + ParameterExpression argsParameterExpression = Expression.Parameter(typeof(object[]), "args"); + + Expression callExpression = BuildMethodCall(method, type, targetParameterExpression, argsParameterExpression); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(MethodCall), callExpression, targetParameterExpression, argsParameterExpression); + + MethodCall compiled = (MethodCall)lambdaExpression.Compile(); + return compiled; + } + + private class ByRefParameter + { + public Expression Value; + public ParameterExpression Variable; + public bool IsOut; + } + + private Expression BuildMethodCall(MethodBase method, Type type, ParameterExpression targetParameterExpression, ParameterExpression argsParameterExpression) + { + ParameterInfo[] parametersInfo = method.GetParameters(); + + Expression[] argsExpression = new Expression[parametersInfo.Length]; + IList refParameterMap = new List(); + + for (int i = 0; i < parametersInfo.Length; i++) + { + ParameterInfo parameter = parametersInfo[i]; + Type parameterType = parameter.ParameterType; + bool isByRef = false; + if (parameterType.IsByRef) + { + parameterType = parameterType.GetElementType(); + isByRef = true; + } + + Expression indexExpression = Expression.Constant(i); + + Expression paramAccessorExpression = Expression.ArrayIndex(argsParameterExpression, indexExpression); + + Expression argExpression; + + if (parameterType.IsValueType()) + { + BinaryExpression ensureValueTypeNotNull = Expression.Coalesce(paramAccessorExpression, Expression.New(parameterType)); + + argExpression = EnsureCastExpression(ensureValueTypeNotNull, parameterType); + } + else + { + argExpression = EnsureCastExpression(paramAccessorExpression, parameterType); + } + + if (isByRef) + { + ParameterExpression variable = Expression.Variable(parameterType); + refParameterMap.Add(new ByRefParameter + { + Value = argExpression, + Variable = variable, + IsOut = parameter.IsOut + }); + + argExpression = variable; + } + + argsExpression[i] = argExpression; + } + + Expression callExpression; + if (method.IsConstructor) + { + callExpression = Expression.New((ConstructorInfo) method, argsExpression); + } + else if (method.IsStatic) + { + callExpression = Expression.Call((MethodInfo) method, argsExpression); + } + else + { + Expression readParameter = EnsureCastExpression(targetParameterExpression, method.DeclaringType); + + callExpression = Expression.Call(readParameter, (MethodInfo) method, argsExpression); + } + + if (method is MethodInfo) + { + MethodInfo m = (MethodInfo) method; + if (m.ReturnType != typeof (void)) + callExpression = EnsureCastExpression(callExpression, type); + else + callExpression = Expression.Block(callExpression, Expression.Constant(null)); + } + else + { + callExpression = EnsureCastExpression(callExpression, type); + } + + if (refParameterMap.Count > 0) + { + IList variableExpressions = new List(); + IList bodyExpressions = new List(); + foreach (ByRefParameter p in refParameterMap) + { + if (!p.IsOut) + bodyExpressions.Add(Expression.Assign(p.Variable, p.Value)); + + variableExpressions.Add(p.Variable); + } + + bodyExpressions.Add(callExpression); + + callExpression = Expression.Block(variableExpressions, bodyExpressions); + } + + return callExpression; + } + + public override Func CreateDefaultConstructor(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + // avoid error from expressions compiler because of abstract class + if (type.IsAbstract()) + return () => (T)Activator.CreateInstance(type); + + try + { + Type resultType = typeof(T); + + Expression expression = Expression.New(type); + + expression = EnsureCastExpression(expression, resultType); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func), expression); + + Func compiled = (Func)lambdaExpression.Compile(); + return compiled; + } + catch + { + // an error can be thrown if constructor is not valid on Win8 + // will have INVOCATION_FLAGS_NON_W8P_FX_API invocation flag + return () => (T)Activator.CreateInstance(type); + } + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + Type instanceType = typeof(T); + Type resultType = typeof(object); + + ParameterExpression parameterExpression = Expression.Parameter(instanceType, "instance"); + Expression resultExpression; + + MethodInfo getMethod = propertyInfo.GetGetMethod(true); + + if (getMethod.IsStatic) + { + resultExpression = Expression.MakeMemberAccess(null, propertyInfo); + } + else + { + Expression readParameter = EnsureCastExpression(parameterExpression, propertyInfo.DeclaringType); + + resultExpression = Expression.MakeMemberAccess(readParameter, propertyInfo); + } + + resultExpression = EnsureCastExpression(resultExpression, resultType); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func), resultExpression, parameterExpression); + + Func compiled = (Func)lambdaExpression.Compile(); + return compiled; + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + ParameterExpression sourceParameter = Expression.Parameter(typeof(T), "source"); + + Expression fieldExpression; + if (fieldInfo.IsStatic) + { + fieldExpression = Expression.Field(null, fieldInfo); + } + else + { + Expression sourceExpression = EnsureCastExpression(sourceParameter, fieldInfo.DeclaringType); + + fieldExpression = Expression.Field(sourceExpression, fieldInfo); + } + + fieldExpression = EnsureCastExpression(fieldExpression, typeof(object)); + + Func compiled = Expression.Lambda>(fieldExpression, sourceParameter).Compile(); + return compiled; + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + // use reflection for structs + // expression doesn't correctly set value + if (fieldInfo.DeclaringType.IsValueType() || fieldInfo.IsInitOnly) + return LateBoundReflectionDelegateFactory.Instance.CreateSet(fieldInfo); + + ParameterExpression sourceParameterExpression = Expression.Parameter(typeof(T), "source"); + ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object), "value"); + + Expression fieldExpression; + if (fieldInfo.IsStatic) + { + fieldExpression = Expression.Field(null, fieldInfo); + } + else + { + Expression sourceExpression = EnsureCastExpression(sourceParameterExpression, fieldInfo.DeclaringType); + + fieldExpression = Expression.Field(sourceExpression, fieldInfo); + } + + Expression valueExpression = EnsureCastExpression(valueParameterExpression, fieldExpression.Type); + + BinaryExpression assignExpression = Expression.Assign(fieldExpression, valueExpression); + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Action), assignExpression, sourceParameterExpression, valueParameterExpression); + + Action compiled = (Action)lambdaExpression.Compile(); + return compiled; + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + // use reflection for structs + // expression doesn't correctly set value + if (propertyInfo.DeclaringType.IsValueType()) + return LateBoundReflectionDelegateFactory.Instance.CreateSet(propertyInfo); + + Type instanceType = typeof(T); + Type valueType = typeof(object); + + ParameterExpression instanceParameter = Expression.Parameter(instanceType, "instance"); + + ParameterExpression valueParameter = Expression.Parameter(valueType, "value"); + Expression readValueParameter = EnsureCastExpression(valueParameter, propertyInfo.PropertyType); + + MethodInfo setMethod = propertyInfo.GetSetMethod(true); + + Expression setExpression; + if (setMethod.IsStatic) + { + setExpression = Expression.Call(setMethod, readValueParameter); + } + else + { + Expression readInstanceParameter = EnsureCastExpression(instanceParameter, propertyInfo.DeclaringType); + + setExpression = Expression.Call(readInstanceParameter, setMethod, readValueParameter); + } + + LambdaExpression lambdaExpression = Expression.Lambda(typeof(Action), setExpression, instanceParameter, valueParameter); + + Action compiled = (Action)lambdaExpression.Compile(); + return compiled; + } + + private Expression EnsureCastExpression(Expression expression, Type targetType) + { + Type expressionType = expression.Type; + + // check if a cast or conversion is required + if (expressionType == targetType || (!expressionType.IsValueType() && targetType.IsAssignableFrom(expressionType))) + return expression; + + return Expression.Convert(expression, targetType); + } + } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/FSharpUtils.cs b/Libs/JsonNet/Utilities/FSharpUtils.cs new file mode 100644 index 0000000..0cedbdb --- /dev/null +++ b/Libs/JsonNet/Utilities/FSharpUtils.cs @@ -0,0 +1,142 @@ +#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.Threading; +#if !(NET35 || NET20 || NETFX_CORE) +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class FSharpUtils + { + private static readonly object Lock = new object(); + + private static bool _initialized; + private static MethodInfo _ofSeq; + private static Type _mapType; + + public static Assembly FSharpCoreAssembly { get; private set; } + public static MethodCall IsUnion { get; private set; } + public static MethodCall GetUnionFields { get; private set; } + public static MethodCall GetUnionCases { get; private set; } + public static MethodCall MakeUnion { get; private set; } + public static Func GetUnionCaseInfoName { get; private set; } + public static Func GetUnionCaseInfo { get; private set; } + public static Func GetUnionCaseFields { get; private set; } + public static MethodCall GetUnionCaseInfoFields { get; private set; } + + public const string FSharpSetTypeName = "FSharpSet`1"; + public const string FSharpListTypeName = "FSharpList`1"; + public const string FSharpMapTypeName = "FSharpMap`2"; + + public static void EnsureInitialized(Assembly fsharpCoreAssembly) + { + if (!_initialized) + { + lock (Lock) + { + if (!_initialized) + { + FSharpCoreAssembly = fsharpCoreAssembly; + + Type fsharpType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Reflection.FSharpType"); + + MethodInfo isUnionMethodInfo = fsharpType.GetMethod("IsUnion", BindingFlags.Public | BindingFlags.Static); + IsUnion = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(isUnionMethodInfo); + + MethodInfo getUnionCasesMethodInfo = fsharpType.GetMethod("GetUnionCases", BindingFlags.Public | BindingFlags.Static); + GetUnionCases = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(getUnionCasesMethodInfo); + + Type fsharpValue = fsharpCoreAssembly.GetType("Microsoft.FSharp.Reflection.FSharpValue"); + + MethodInfo getUnionFieldsMethodInfo = fsharpValue.GetMethod("GetUnionFields", BindingFlags.Public | BindingFlags.Static); + GetUnionFields = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(getUnionFieldsMethodInfo); + + GetUnionCaseInfo = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(getUnionFieldsMethodInfo.ReturnType.GetProperty("Item1")); + GetUnionCaseFields = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(getUnionFieldsMethodInfo.ReturnType.GetProperty("Item2")); + + MethodInfo makeUnionMethodInfo = fsharpValue.GetMethod("MakeUnion", BindingFlags.Public | BindingFlags.Static); + MakeUnion = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(makeUnionMethodInfo); + + Type unionCaseInfo = fsharpCoreAssembly.GetType("Microsoft.FSharp.Reflection.UnionCaseInfo"); + + GetUnionCaseInfoName = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(unionCaseInfo.GetProperty("Name")); + GetUnionCaseInfoFields = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(unionCaseInfo.GetMethod("GetFields")); + + Type listModule = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.ListModule"); + _ofSeq = listModule.GetMethod("OfSeq"); + + _mapType = fsharpCoreAssembly.GetType("Microsoft.FSharp.Collections.FSharpMap`2"); + +#if !(NETFX_CORE || PORTABLE) + Thread.MemoryBarrier(); +#endif + _initialized = true; + } + } + } + } + + public static ObjectConstructor CreateSeq(Type t) + { + MethodInfo seqType = _ofSeq.MakeGenericMethod(t); + + return JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(seqType); + } + + public static ObjectConstructor CreateMap(Type keyType, Type valueType) + { + MethodInfo creatorDefinition = typeof (FSharpUtils).GetMethod("BuildMapCreator"); + + MethodInfo creatorGeneric = creatorDefinition.MakeGenericMethod(keyType, valueType); + + return (ObjectConstructor)creatorGeneric.Invoke(null, null); + } + + public static ObjectConstructor BuildMapCreator() + { + Type genericMapType = _mapType.MakeGenericType(typeof(TKey), typeof(TValue)); + ConstructorInfo ctor = genericMapType.GetConstructor(new[] { typeof(IEnumerable>) }); + ObjectConstructor ctorDelegate = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(ctor); + + ObjectConstructor creator = args => + { + // convert dictionary KeyValuePairs to Tuples + IEnumerable> values = (IEnumerable>)args[0]; + IEnumerable> tupleValues = values.Select(kv => new Tuple(kv.Key, kv.Value)); + + return ctorDelegate(tupleValues); + }; + + return creator; + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ILGeneratorExtensions.cs b/Libs/JsonNet/Utilities/ILGeneratorExtensions.cs new file mode 100644 index 0000000..30fd02b --- /dev/null +++ b/Libs/JsonNet/Utilities/ILGeneratorExtensions.cs @@ -0,0 +1,81 @@ +#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 !(PORTABLE40 || PORTABLE || NETFX_CORE) +using System; +using System.Reflection.Emit; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ILGeneratorExtensions + { + public static void PushInstance(this ILGenerator generator, Type type) + { + generator.Emit(OpCodes.Ldarg_0); + if (type.IsValueType()) + generator.Emit(OpCodes.Unbox, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void PushArrayInstance(this ILGenerator generator, int argsIndex, int arrayIndex) + { + generator.Emit(OpCodes.Ldarg, argsIndex); + generator.Emit(OpCodes.Ldc_I4, arrayIndex); + generator.Emit(OpCodes.Ldelem_Ref); + } + + public static void BoxIfNeeded(this ILGenerator generator, Type type) + { + if (type.IsValueType()) + generator.Emit(OpCodes.Box, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void UnboxIfNeeded(this ILGenerator generator, Type type) + { + if (type.IsValueType()) + generator.Emit(OpCodes.Unbox_Any, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void CallMethod(this ILGenerator generator, MethodInfo methodInfo) + { + if (methodInfo.IsFinal || !methodInfo.IsVirtual) + generator.Emit(OpCodes.Call, methodInfo); + else + generator.Emit(OpCodes.Callvirt, methodInfo); + } + + public static void Return(this ILGenerator generator) + { + generator.Emit(OpCodes.Ret); + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ImmutableCollectionsUtils.cs b/Libs/JsonNet/Utilities/ImmutableCollectionsUtils.cs new file mode 100644 index 0000000..f18d844 --- /dev/null +++ b/Libs/JsonNet/Utilities/ImmutableCollectionsUtils.cs @@ -0,0 +1,175 @@ +#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 !(NET20 || NET35 || NET40 || PORTABLE40) +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ImmutableCollectionsUtils + { + internal class ImmutableCollectionTypeInfo + { + public ImmutableCollectionTypeInfo(string contractTypeName, string createdTypeName, string builderTypeName) + { + ContractTypeName = contractTypeName; + CreatedTypeName = createdTypeName; + BuilderTypeName = builderTypeName; + } + + public string ContractTypeName { get; set; } + public string CreatedTypeName { get; set; } + public string BuilderTypeName { get; set; } + } + + private const string ImmutableListGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableList`1"; + private const string ImmutableQueueGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableQueue`1"; + private const string ImmutableStackGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableStack`1"; + private const string ImmutableSetGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableSet`1"; + + private const string ImmutableArrayTypeName = "System.Collections.Immutable.ImmutableArray"; + private const string ImmutableArrayGenericTypeName = "System.Collections.Immutable.ImmutableArray`1"; + + private const string ImmutableListTypeName = "System.Collections.Immutable.ImmutableList"; + private const string ImmutableListGenericTypeName = "System.Collections.Immutable.ImmutableList`1"; + + private const string ImmutableQueueTypeName = "System.Collections.Immutable.ImmutableQueue"; + private const string ImmutableQueueGenericTypeName = "System.Collections.Immutable.ImmutableQueue`1"; + + private const string ImmutableStackTypeName = "System.Collections.Immutable.ImmutableStack"; + private const string ImmutableStackGenericTypeName = "System.Collections.Immutable.ImmutableStack`1"; + + private const string ImmutableSortedSetTypeName = "System.Collections.Immutable.ImmutableSortedSet"; + private const string ImmutableSortedSetGenericTypeName = "System.Collections.Immutable.ImmutableSortedSet`1"; + + private const string ImmutableHashSetTypeName = "System.Collections.Immutable.ImmutableHashSet"; + private const string ImmutableHashSetGenericTypeName = "System.Collections.Immutable.ImmutableHashSet`1"; + + private static readonly IList ArrayContractImmutableCollectionDefinitions = new List + { + new ImmutableCollectionTypeInfo(ImmutableListGenericInterfaceTypeName, ImmutableListGenericTypeName, ImmutableListTypeName), + new ImmutableCollectionTypeInfo(ImmutableListGenericTypeName, ImmutableListGenericTypeName, ImmutableListTypeName), + new ImmutableCollectionTypeInfo(ImmutableQueueGenericInterfaceTypeName, ImmutableQueueGenericTypeName, ImmutableQueueTypeName), + new ImmutableCollectionTypeInfo(ImmutableQueueGenericTypeName, ImmutableQueueGenericTypeName, ImmutableQueueTypeName), + new ImmutableCollectionTypeInfo(ImmutableStackGenericInterfaceTypeName, ImmutableStackGenericTypeName, ImmutableStackTypeName), + new ImmutableCollectionTypeInfo(ImmutableStackGenericTypeName, ImmutableStackGenericTypeName, ImmutableStackTypeName), + new ImmutableCollectionTypeInfo(ImmutableSetGenericInterfaceTypeName, ImmutableSortedSetGenericTypeName, ImmutableSortedSetTypeName), + new ImmutableCollectionTypeInfo(ImmutableSortedSetGenericTypeName, ImmutableSortedSetGenericTypeName, ImmutableSortedSetTypeName), + new ImmutableCollectionTypeInfo(ImmutableHashSetGenericTypeName, ImmutableHashSetGenericTypeName, ImmutableHashSetTypeName), + new ImmutableCollectionTypeInfo(ImmutableArrayGenericTypeName, ImmutableArrayGenericTypeName, ImmutableArrayTypeName) + }; + + private const string ImmutableDictionaryGenericInterfaceTypeName = "System.Collections.Immutable.IImmutableDictionary`2"; + + private const string ImmutableDictionaryTypeName = "System.Collections.Immutable.ImmutableDictionary"; + private const string ImmutableDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableDictionary`2"; + + private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary"; + private const string ImmutableSortedDictionaryGenericTypeName = "System.Collections.Immutable.ImmutableSortedDictionary`2"; + + private static readonly IList DictionaryContractImmutableCollectionDefinitions = new List + { + new ImmutableCollectionTypeInfo(ImmutableDictionaryGenericInterfaceTypeName, ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryTypeName), + new ImmutableCollectionTypeInfo(ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryGenericTypeName, ImmutableSortedDictionaryTypeName), + new ImmutableCollectionTypeInfo(ImmutableDictionaryGenericTypeName, ImmutableDictionaryGenericTypeName, ImmutableDictionaryTypeName) + }; + + internal static bool TryBuildImmutableForArrayContract(Type underlyingType, Type collectionItemType, out Type createdType, out ObjectConstructor parameterizedCreator) + { + if (underlyingType.IsGenericType()) + { + Type underlyingTypeDefinition = underlyingType.GetGenericTypeDefinition(); + string name = underlyingTypeDefinition.FullName; + + ImmutableCollectionTypeInfo definition = ArrayContractImmutableCollectionDefinitions.FirstOrDefault(d => d.ContractTypeName == name); + if (definition != null) + { + Type createdTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.CreatedTypeName); + Type builderTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.BuilderTypeName); + + if (createdTypeDefinition != null && builderTypeDefinition != null) + { + MethodInfo mb = builderTypeDefinition.GetMethods().FirstOrDefault(m => m.Name == "CreateRange" && m.GetParameters().Length == 1); + if (mb != null) + { + createdType = createdTypeDefinition.MakeGenericType(collectionItemType); + MethodInfo method = mb.MakeGenericMethod(collectionItemType); + parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(method); + return true; + } + } + } + } + + createdType = null; + parameterizedCreator = null; + return false; + } + + internal static bool TryBuildImmutableForDictionaryContract(Type underlyingType, Type keyItemType, Type valueItemType, out Type createdType, out ObjectConstructor parameterizedCreator) + { + if (underlyingType.IsGenericType()) + { + Type underlyingTypeDefinition = underlyingType.GetGenericTypeDefinition(); + string name = underlyingTypeDefinition.FullName; + + ImmutableCollectionTypeInfo definition = DictionaryContractImmutableCollectionDefinitions.FirstOrDefault(d => d.ContractTypeName == name); + if (definition != null) + { + Type createdTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.CreatedTypeName); + Type builderTypeDefinition = underlyingTypeDefinition.Assembly().GetType(definition.BuilderTypeName); + + if (createdTypeDefinition != null && builderTypeDefinition != null) + { + MethodInfo mb = builderTypeDefinition.GetMethods().FirstOrDefault(m => + { + ParameterInfo[] parameters = m.GetParameters(); + + return m.Name == "CreateRange" && parameters.Length == 1 && parameters[0].ParameterType.IsGenericType() && parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>); + }); + if (mb != null) + { + createdType = createdTypeDefinition.MakeGenericType(keyItemType, valueItemType); + MethodInfo method = mb.MakeGenericMethod(keyItemType, valueItemType); + parameterizedCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateParametrizedConstructor(method); + return true; + } + } + } + } + + createdType = null; + parameterizedCreator = null; + return false; + } + } +} +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/JavaScriptUtils.cs b/Libs/JsonNet/Utilities/JavaScriptUtils.cs new file mode 100644 index 0000000..378db19 --- /dev/null +++ b/Libs/JsonNet/Utilities/JavaScriptUtils.cs @@ -0,0 +1,254 @@ +#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; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; + +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal static class JavaScriptUtils + { + internal static readonly bool[] SingleQuoteCharEscapeFlags = new bool[128]; + internal static readonly bool[] DoubleQuoteCharEscapeFlags = new bool[128]; + internal static readonly bool[] HtmlCharEscapeFlags = new bool[128]; + + static JavaScriptUtils() + { + IList escapeChars = new List + { + '\n', '\r', '\t', '\\', '\f', '\b', + }; + for (int i = 0; i < ' '; i++) + { + escapeChars.Add((char)i); + } + + foreach (var escapeChar in escapeChars.Union(new[] { '\'' })) + { + SingleQuoteCharEscapeFlags[escapeChar] = true; + } + foreach (var escapeChar in escapeChars.Union(new[] { '"' })) + { + DoubleQuoteCharEscapeFlags[escapeChar] = true; + } + foreach (var escapeChar in escapeChars.Union(new[] { '"', '\'', '<', '>', '&' })) + { + HtmlCharEscapeFlags[escapeChar] = true; + } + } + + private const string EscapedUnicodeText = "!"; + + public static bool[] GetCharEscapeFlags(StringEscapeHandling stringEscapeHandling, char quoteChar) + { + if (stringEscapeHandling == StringEscapeHandling.EscapeHtml) + return HtmlCharEscapeFlags; + + if (quoteChar == '"') + return DoubleQuoteCharEscapeFlags; + + return SingleQuoteCharEscapeFlags; + } + + public static bool ShouldEscapeJavaScriptString(string s, bool[] charEscapeFlags) + { + if (s == null) + return false; + + foreach (char c in s) + { + if (c >= charEscapeFlags.Length || charEscapeFlags[c]) + return true; + } + + return false; + } + + public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters, + bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, ref char[] writeBuffer) + { + // leading delimiter + if (appendDelimiters) + writer.Write(delimiter); + + if (s != null) + { + int lastWritePosition = 0; + + for (int i = 0; i < s.Length; i++) + { + var c = s[i]; + + if (c < charEscapeFlags.Length && !charEscapeFlags[c]) + continue; + + string escapedValue; + + switch (c) + { + case '\t': + escapedValue = @"\t"; + break; + case '\n': + escapedValue = @"\n"; + break; + case '\r': + escapedValue = @"\r"; + break; + case '\f': + escapedValue = @"\f"; + break; + case '\b': + escapedValue = @"\b"; + break; + case '\\': + escapedValue = @"\\"; + break; + case '\u0085': // Next Line + escapedValue = @"\u0085"; + break; + case '\u2028': // Line Separator + escapedValue = @"\u2028"; + break; + case '\u2029': // Paragraph Separator + escapedValue = @"\u2029"; + break; + default: + if (c < charEscapeFlags.Length || stringEscapeHandling == StringEscapeHandling.EscapeNonAscii) + { + if (c == '\'' && stringEscapeHandling != StringEscapeHandling.EscapeHtml) + { + escapedValue = @"\'"; + } + else if (c == '"' && stringEscapeHandling != StringEscapeHandling.EscapeHtml) + { + escapedValue = @"\"""; + } + else + { + if (writeBuffer == null) + writeBuffer = new char[6]; + + StringUtils.ToCharAsUnicode(c, writeBuffer); + + // slightly hacky but it saves multiple conditions in if test + escapedValue = EscapedUnicodeText; + } + } + else + { + escapedValue = null; + } + break; + } + + if (escapedValue == null) + continue; + + bool isEscapedUnicodeText = string.Equals(escapedValue, EscapedUnicodeText); + + if (i > lastWritePosition) + { + int length = i - lastWritePosition + ((isEscapedUnicodeText) ? 6 : 0); + int start = (isEscapedUnicodeText) ? 6 : 0; + + if (writeBuffer == null || writeBuffer.Length < length) + { + char[] newBuffer = new char[length]; + + // the unicode text is already in the buffer + // copy it over when creating new buffer + if (isEscapedUnicodeText) + Array.Copy(writeBuffer, newBuffer, 6); + + writeBuffer = newBuffer; + } + + s.CopyTo(lastWritePosition, writeBuffer, start, length - start); + + // write unchanged chars before writing escaped text + writer.Write(writeBuffer, start, length - start); + } + + lastWritePosition = i + 1; + if (!isEscapedUnicodeText) + writer.Write(escapedValue); + else + writer.Write(writeBuffer, 0, 6); + } + + if (lastWritePosition == 0) + { + // no escaped text, write entire string + writer.Write(s); + } + else + { + int length = s.Length - lastWritePosition; + + if (writeBuffer == null || writeBuffer.Length < length) + writeBuffer = new char[length]; + + s.CopyTo(lastWritePosition, writeBuffer, 0, length); + + // write remaining text + writer.Write(writeBuffer, 0, length); + } + } + + // trailing delimiter + if (appendDelimiters) + writer.Write(delimiter); + } + + public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters) + { + return ToEscapedJavaScriptString(value, delimiter, appendDelimiters, StringEscapeHandling.Default); + } + + public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters, StringEscapeHandling stringEscapeHandling) + { + bool[] charEscapeFlags = GetCharEscapeFlags(stringEscapeHandling, delimiter); + + using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16)) + { + char[] buffer = null; + WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, charEscapeFlags, stringEscapeHandling, ref buffer); + return w.ToString(); + } + } + } +} diff --git a/Libs/JsonNet/Utilities/LateBoundReflectionDelegateFactory.cs b/Libs/JsonNet/Utilities/LateBoundReflectionDelegateFactory.cs new file mode 100644 index 0000000..2ebf601 --- /dev/null +++ b/Libs/JsonNet/Utilities/LateBoundReflectionDelegateFactory.cs @@ -0,0 +1,107 @@ +#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 Newtonsoft.Json.Serialization; +using System.Reflection; + +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal class LateBoundReflectionDelegateFactory : ReflectionDelegateFactory + { + private static readonly LateBoundReflectionDelegateFactory _instance = new LateBoundReflectionDelegateFactory(); + + internal static ReflectionDelegateFactory Instance + { + get { return _instance; } + } + + public override ObjectConstructor CreateParametrizedConstructor(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + ConstructorInfo c = method as ConstructorInfo; + if (c != null) + return c.Invoke; + + return a => method.Invoke(null, a); + } + + public override MethodCall CreateMethodCall(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + ConstructorInfo c = method as ConstructorInfo; + if (c != null) + return (o, a) => c.Invoke(a); + + return (o, a) => method.Invoke(o, a); + } + + public override Func CreateDefaultConstructor(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (type.IsValueType()) + return () => (T)Activator.CreateInstance(type); + + ConstructorInfo constructorInfo = ReflectionUtils.GetDefaultConstructor(type, true); + + return () => (T)constructorInfo.Invoke(null); + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + return o => propertyInfo.GetValue(o, null); + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + return o => fieldInfo.GetValue(o); + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + return (o, v) => fieldInfo.SetValue(o, v); + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + return (o, v) => propertyInfo.SetValue(o, v, null); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/LinqBridge.cs b/Libs/JsonNet/Utilities/LinqBridge.cs new file mode 100644 index 0000000..f1a0807 --- /dev/null +++ b/Libs/JsonNet/Utilities/LinqBridge.cs @@ -0,0 +1,3014 @@ + +#if NET20 + +#region License, Terms and Author(s) +// +// LINQBridge +// Copyright (c) 2007-9 Atif Aziz, Joseph Albahari. All rights reserved. +// +// Author(s): +// +// Atif Aziz, http://www.raboof.com +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the New BSD License, a copy of which should have +// been delivered along with this distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities.LinqBridge +{ + /// + /// Provides a set of static (Shared in Visual Basic) methods for + /// querying objects that implement . + /// + internal static partial class Enumerable + { + /// + /// Returns the input typed as . + /// + + public static IEnumerable AsEnumerable(IEnumerable source) + { + return source; + } + + /// + /// Returns an empty that has the + /// specified type argument. + /// + + public static IEnumerable Empty() + { + return Sequence.Empty; + } + + /// + /// Converts the elements of an to the + /// specified type. + /// + + public static IEnumerable Cast( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return CastYield(source); + } + + private static IEnumerable CastYield( + IEnumerable source) + { + foreach (var item in source) + yield return (TResult) item; + } + + /// + /// Filters the elements of an based on a specified type. + /// + + public static IEnumerable OfType( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return OfTypeYield(source); + } + + private static IEnumerable OfTypeYield( + IEnumerable source) + { + foreach (var item in source) + if (item is TResult) + yield return (TResult) item; + } + + /// + /// Generates a sequence of integral numbers within a specified range. + /// + /// The value of the first integer in the sequence. + /// The number of sequential integers to generate. + + public static IEnumerable Range(int start, int count) + { + if (count < 0) + throw new ArgumentOutOfRangeException("count", count, null); + + var end = (long) start + count; + if (end - 1 >= int.MaxValue) + throw new ArgumentOutOfRangeException("count", count, null); + + return RangeYield(start, end); + } + + private static IEnumerable RangeYield(int start, long end) + { + for (var i = start; i < end; i++) + yield return i; + } + + /// + /// Generates a sequence that contains one repeated value. + /// + + public static IEnumerable Repeat(TResult element, int count) + { + if (count < 0) throw new ArgumentOutOfRangeException("count", count, null); + + return RepeatYield(element, count); + } + + private static IEnumerable RepeatYield(TResult element, int count) + { + for (var i = 0; i < count; i++) + yield return element; + } + + /// + /// Filters a sequence of values based on a predicate. + /// + + public static IEnumerable Where( + this IEnumerable source, + Func predicate) + { + CheckNotNull(predicate, "predicate"); + + return source.Where((item, i) => predicate(item)); + } + + /// + /// Filters a sequence of values based on a predicate. + /// Each element's index is used in the logic of the predicate function. + /// + + public static IEnumerable Where( + this IEnumerable source, + Func predicate) + { + CheckNotNull(source, "source"); + CheckNotNull(predicate, "predicate"); + + return WhereYield(source, predicate); + } + + private static IEnumerable WhereYield( + IEnumerable source, + Func predicate) + { + var i = 0; + foreach (var item in source) + if (predicate(item, i++)) + yield return item; + } + + /// + /// Projects each element of a sequence into a new form. + /// + + public static IEnumerable Select( + this IEnumerable source, + Func selector) + { + CheckNotNull(selector, "selector"); + + return source.Select((item, i) => selector(item)); + } + + /// + /// Projects each element of a sequence into a new form by + /// incorporating the element's index. + /// + + public static IEnumerable Select( + this IEnumerable source, + Func selector) + { + CheckNotNull(source, "source"); + CheckNotNull(selector, "selector"); + + return SelectYield(source, selector); + } + + private static IEnumerable SelectYield( + IEnumerable source, + Func selector) + { + var i = 0; + foreach (var item in source) + yield return selector(item, i++); + } + + /// + /// Projects each element of a sequence to an + /// and flattens the resulting sequences into one sequence. + /// + + public static IEnumerable SelectMany( + this IEnumerable source, + Func> selector) + { + CheckNotNull(selector, "selector"); + + return source.SelectMany((item, i) => selector(item)); + } + + /// + /// Projects each element of a sequence to an , + /// and flattens the resulting sequences into one sequence. The + /// index of each source element is used in the projected form of + /// that element. + /// + + public static IEnumerable SelectMany( + this IEnumerable source, + Func> selector) + { + CheckNotNull(selector, "selector"); + + return source.SelectMany(selector, (item, subitem) => subitem); + } + + /// + /// Projects each element of a sequence to an , + /// flattens the resulting sequences into one sequence, and invokes + /// a result selector function on each element therein. + /// + + public static IEnumerable SelectMany( + this IEnumerable source, + Func> collectionSelector, + Func resultSelector) + { + CheckNotNull(collectionSelector, "collectionSelector"); + + return source.SelectMany((item, i) => collectionSelector(item), resultSelector); + } + + /// + /// Projects each element of a sequence to an , + /// flattens the resulting sequences into one sequence, and invokes + /// a result selector function on each element therein. The index of + /// each source element is used in the intermediate projected form + /// of that element. + /// + + public static IEnumerable SelectMany( + this IEnumerable source, + Func> collectionSelector, + Func resultSelector) + { + CheckNotNull(source, "source"); + CheckNotNull(collectionSelector, "collectionSelector"); + CheckNotNull(resultSelector, "resultSelector"); + + return SelectManyYield(source, collectionSelector, resultSelector); + } + + private static IEnumerable SelectManyYield( + this IEnumerable source, + Func> collectionSelector, + Func resultSelector) + { + var i = 0; + foreach (var item in source) + foreach (var subitem in collectionSelector(item, i++)) + yield return resultSelector(item, subitem); + } + + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// + + public static IEnumerable TakeWhile( + this IEnumerable source, + Func predicate) + { + CheckNotNull(predicate, "predicate"); + + return source.TakeWhile((item, i) => predicate(item)); + } + + /// + /// Returns elements from a sequence as long as a specified condition is true. + /// The element's index is used in the logic of the predicate function. + /// + + public static IEnumerable TakeWhile( + this IEnumerable source, + Func predicate) + { + CheckNotNull(source, "source"); + CheckNotNull(predicate, "predicate"); + + return TakeWhileYield(source, predicate); + } + + private static IEnumerable TakeWhileYield( + this IEnumerable source, + Func predicate) + { + var i = 0; + foreach (var item in source) + if (predicate(item, i++)) + yield return item; + else + break; + } + + private static class Futures + { + public static readonly Func Default = () => default(T); + public static readonly Func Undefined = () => { throw new InvalidOperationException(); }; + } + + /// + /// Base implementation of First operator. + /// + + private static TSource FirstImpl( + this IEnumerable source, + Func empty) + { + CheckNotNull(source, "source"); + Debug.Assert(empty != null); + + var list = source as IList; // optimized case for lists + if (list != null) + return list.Count > 0 ? list[0] : empty(); + + using (var e = source.GetEnumerator()) // fallback for enumeration + return e.MoveNext() ? e.Current : empty(); + } + + /// + /// Returns the first element of a sequence. + /// + + public static TSource First( + this IEnumerable source) + { + return source.FirstImpl(Futures.Undefined); + } + + /// + /// Returns the first element in a sequence that satisfies a specified condition. + /// + + public static TSource First( + this IEnumerable source, + Func predicate) + { + return First(source.Where(predicate)); + } + + /// + /// Returns the first element of a sequence, or a default value if + /// the sequence contains no elements. + /// + + public static TSource FirstOrDefault( + this IEnumerable source) + { + return source.FirstImpl(Futures.Default); + } + + /// + /// Returns the first element of the sequence that satisfies a + /// condition or a default value if no such element is found. + /// + + public static TSource FirstOrDefault( + this IEnumerable source, + Func predicate) + { + return FirstOrDefault(source.Where(predicate)); + } + + /// + /// Base implementation of Last operator. + /// + + private static TSource LastImpl( + this IEnumerable source, + Func empty) + { + CheckNotNull(source, "source"); + + var list = source as IList; // optimized case for lists + if (list != null) + return list.Count > 0 ? list[list.Count - 1] : empty(); + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + return empty(); + + var last = e.Current; + while (e.MoveNext()) + last = e.Current; + + return last; + } + } + + /// + /// Returns the last element of a sequence. + /// + public static TSource Last( + this IEnumerable source) + { + return source.LastImpl(Futures.Undefined); + } + + /// + /// Returns the last element of a sequence that satisfies a + /// specified condition. + /// + + public static TSource Last( + this IEnumerable source, + Func predicate) + { + return Last(source.Where(predicate)); + } + + /// + /// Returns the last element of a sequence, or a default value if + /// the sequence contains no elements. + /// + + public static TSource LastOrDefault( + this IEnumerable source) + { + return source.LastImpl(Futures.Default); + } + + /// + /// Returns the last element of a sequence that satisfies a + /// condition or a default value if no such element is found. + /// + + public static TSource LastOrDefault( + this IEnumerable source, + Func predicate) + { + return LastOrDefault(source.Where(predicate)); + } + + /// + /// Base implementation of Single operator. + /// + + private static TSource SingleImpl( + this IEnumerable source, + Func empty) + { + CheckNotNull(source, "source"); + + using (var e = source.GetEnumerator()) + { + if (e.MoveNext()) + { + var single = e.Current; + if (!e.MoveNext()) + return single; + + throw new InvalidOperationException(); + } + + return empty(); + } + } + + /// + /// Returns the only element of a sequence, and throws an exception + /// if there is not exactly one element in the sequence. + /// + + public static TSource Single( + this IEnumerable source) + { + return source.SingleImpl(Futures.Undefined); + } + + /// + /// Returns the only element of a sequence that satisfies a + /// specified condition, and throws an exception if more than one + /// such element exists. + /// + + public static TSource Single( + this IEnumerable source, + Func predicate) + { + return Single(source.Where(predicate)); + } + + /// + /// Returns the only element of a sequence, or a default value if + /// the sequence is empty; this method throws an exception if there + /// is more than one element in the sequence. + /// + + public static TSource SingleOrDefault( + this IEnumerable source) + { + return source.SingleImpl(Futures.Default); + } + + /// + /// Returns the only element of a sequence that satisfies a + /// specified condition or a default value if no such element + /// exists; this method throws an exception if more than one element + /// satisfies the condition. + /// + + public static TSource SingleOrDefault( + this IEnumerable source, + Func predicate) + { + return SingleOrDefault(source.Where(predicate)); + } + + /// + /// Returns the element at a specified index in a sequence. + /// + + public static TSource ElementAt( + this IEnumerable source, + int index) + { + CheckNotNull(source, "source"); + + if (index < 0) + throw new ArgumentOutOfRangeException("index", index, null); + + var list = source as IList; + if (list != null) + return list[index]; + + try + { + return source.SkipWhile((item, i) => i < index).First(); + } + catch (InvalidOperationException) // if thrown by First + { + throw new ArgumentOutOfRangeException("index", index, null); + } + } + + /// + /// Returns the element at a specified index in a sequence or a + /// default value if the index is out of range. + /// + + public static TSource ElementAtOrDefault( + this IEnumerable source, + int index) + { + CheckNotNull(source, "source"); + + if (index < 0) + return default(TSource); + + var list = source as IList; + if (list != null) + return index < list.Count ? list[index] : default(TSource); + + return source.SkipWhile((item, i) => i < index).FirstOrDefault(); + } + + /// + /// Inverts the order of the elements in a sequence. + /// + + public static IEnumerable Reverse( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return ReverseYield(source); + } + + private static IEnumerable ReverseYield(IEnumerable source) + { + var stack = new Stack(); + foreach (var item in source) + stack.Push(item); + + foreach (var item in stack) + yield return item; + } + + /// + /// Returns a specified number of contiguous elements from the start + /// of a sequence. + /// + + public static IEnumerable Take( + this IEnumerable source, + int count) + { + return source.Where((item, i) => i < count); + } + + /// + /// Bypasses a specified number of elements in a sequence and then + /// returns the remaining elements. + /// + + public static IEnumerable Skip( + this IEnumerable source, + int count) + { + return source.Where((item, i) => i >= count); + } + + /// + /// Bypasses elements in a sequence as long as a specified condition + /// is true and then returns the remaining elements. + /// + + public static IEnumerable SkipWhile( + this IEnumerable source, + Func predicate) + { + CheckNotNull(predicate, "predicate"); + + return source.SkipWhile((item, i) => predicate(item)); + } + + /// + /// Bypasses elements in a sequence as long as a specified condition + /// is true and then returns the remaining elements. The element's + /// index is used in the logic of the predicate function. + /// + + public static IEnumerable SkipWhile( + this IEnumerable source, + Func predicate) + { + CheckNotNull(source, "source"); + CheckNotNull(predicate, "predicate"); + + return SkipWhileYield(source, predicate); + } + + private static IEnumerable SkipWhileYield( + IEnumerable source, + Func predicate) + { + using (var e = source.GetEnumerator()) + { + for (var i = 0;; i++) + { + if (!e.MoveNext()) + yield break; + + if (!predicate(e.Current, i)) + break; + } + + do + { + yield return e.Current; + } while (e.MoveNext()); + } + } + + /// + /// Returns the number of elements in a sequence. + /// + + public static int Count( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + var collection = source as ICollection; + return collection != null + ? collection.Count + : source.Aggregate(0, (count, item) => checked(count + 1)); + } + + /// + /// Returns a number that represents how many elements in the + /// specified sequence satisfy a condition. + /// + + public static int Count( + this IEnumerable source, + Func predicate) + { + return Count(source.Where(predicate)); + } + + /// + /// Returns an that represents the total number + /// of elements in a sequence. + /// + + public static long LongCount( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + var array = source as Array; + return array != null + ? array.LongLength + : source.Aggregate(0L, (count, item) => count + 1); + } + + /// + /// Returns an that represents how many elements + /// in a sequence satisfy a condition. + /// + + public static long LongCount( + this IEnumerable source, + Func predicate) + { + return LongCount(source.Where(predicate)); + } + + /// + /// Concatenates two sequences. + /// + + public static IEnumerable Concat( + this IEnumerable first, + IEnumerable second) + { + CheckNotNull(first, "first"); + CheckNotNull(second, "second"); + + return ConcatYield(first, second); + } + + private static IEnumerable ConcatYield( + IEnumerable first, + IEnumerable second) + { + foreach (var item in first) + yield return item; + + foreach (var item in second) + yield return item; + } + + /// + /// Creates a from an . + /// + + public static List ToList( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return new List(source); + } + + /// + /// Creates an array from an . + /// + + public static TSource[] ToArray( + this IEnumerable source) + { + return source.ToList().ToArray(); + } + + /// + /// Returns distinct elements from a sequence by using the default + /// equality comparer to compare values. + /// + + public static IEnumerable Distinct( + this IEnumerable source) + { + return Distinct(source, /* comparer */ null); + } + + /// + /// Returns distinct elements from a sequence by using a specified + /// to compare values. + /// + + public static IEnumerable Distinct( + this IEnumerable source, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + + return DistinctYield(source, comparer); + } + + private static IEnumerable DistinctYield( + IEnumerable source, + IEqualityComparer comparer) + { + var set = new Dictionary(comparer); + var gotNull = false; + + foreach (var item in source) + { + if (item == null) + { + if (gotNull) + continue; + gotNull = true; + } + else + { + if (set.ContainsKey(item)) + continue; + set.Add(item, null); + } + + yield return item; + } + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function. + /// + + public static ILookup ToLookup( + this IEnumerable source, + Func keySelector) + { + return ToLookup(source, keySelector, e => e, /* comparer */ null); + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function and a key comparer. + /// + + public static ILookup ToLookup( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return ToLookup(source, keySelector, e => e, comparer); + } + + /// + /// Creates a from an + /// according to specified key + /// and element selector functions. + /// + + public static ILookup ToLookup( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return ToLookup(source, keySelector, elementSelector, /* comparer */ null); + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function, a comparer and an element selector function. + /// + + public static ILookup ToLookup( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + CheckNotNull(elementSelector, "elementSelector"); + + var lookup = new Lookup(comparer); + + foreach (var item in source) + { + var key = keySelector(item); + + var grouping = (Grouping) lookup.Find(key); + if (grouping == null) + { + grouping = new Grouping(key); + lookup.Add(grouping); + } + + grouping.Add(elementSelector(item)); + } + + return lookup; + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function. + /// + + public static IEnumerable> GroupBy( + this IEnumerable source, + Func keySelector) + { + return GroupBy(source, keySelector, /* comparer */ null); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and compares the keys by using a specified + /// comparer. + /// + + public static IEnumerable> GroupBy( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return GroupBy(source, keySelector, e => e, comparer); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and projects the elements for each group by + /// using a specified function. + /// + + public static IEnumerable> GroupBy( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return GroupBy(source, keySelector, elementSelector, /* comparer */ null); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and creates a result value from each group and + /// its key. + /// + + public static IEnumerable> GroupBy( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + CheckNotNull(elementSelector, "elementSelector"); + + return ToLookup(source, keySelector, elementSelector, comparer); + } + + /// + /// Groups the elements of a sequence according to a key selector + /// function. The keys are compared by using a comparer and each + /// group's elements are projected by using a specified function. + /// + + public static IEnumerable GroupBy( + this IEnumerable source, + Func keySelector, + Func, TResult> resultSelector) + { + return GroupBy(source, keySelector, resultSelector, /* comparer */ null); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and creates a result value from each group and + /// its key. The elements of each group are projected by using a + /// specified function. + /// + + public static IEnumerable GroupBy( + this IEnumerable source, + Func keySelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + CheckNotNull(resultSelector, "resultSelector"); + + return ToLookup(source, keySelector, comparer).Select(g => resultSelector(g.Key, g)); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and creates a result value from each group and + /// its key. The keys are compared by using a specified comparer. + /// + + public static IEnumerable GroupBy( + this IEnumerable source, + Func keySelector, + Func elementSelector, + Func, TResult> resultSelector) + { + return GroupBy(source, keySelector, elementSelector, resultSelector, /* comparer */ null); + } + + /// + /// Groups the elements of a sequence according to a specified key + /// selector function and creates a result value from each group and + /// its key. Key values are compared by using a specified comparer, + /// and the elements of each group are projected by using a + /// specified function. + /// + + public static IEnumerable GroupBy( + this IEnumerable source, + Func keySelector, + Func elementSelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + CheckNotNull(elementSelector, "elementSelector"); + CheckNotNull(resultSelector, "resultSelector"); + + return ToLookup(source, keySelector, elementSelector, comparer) + .Select(g => resultSelector(g.Key, g)); + } + + /// + /// Applies an accumulator function over a sequence. + /// + + public static TSource Aggregate( + this IEnumerable source, + Func func) + { + CheckNotNull(source, "source"); + CheckNotNull(func, "func"); + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + throw new InvalidOperationException(); + + return e.Renumerable().Skip(1).Aggregate(e.Current, func); + } + } + + /// + /// Applies an accumulator function over a sequence. The specified + /// seed value is used as the initial accumulator value. + /// + + public static TAccumulate Aggregate( + this IEnumerable source, + TAccumulate seed, + Func func) + { + return Aggregate(source, seed, func, r => r); + } + + /// + /// Applies an accumulator function over a sequence. The specified + /// seed value is used as the initial accumulator value, and the + /// specified function is used to select the result value. + /// + + public static TResult Aggregate( + this IEnumerable source, + TAccumulate seed, + Func func, + Func resultSelector) + { + CheckNotNull(source, "source"); + CheckNotNull(func, "func"); + CheckNotNull(resultSelector, "resultSelector"); + + var result = seed; + + foreach (var item in source) + result = func(result, item); + + return resultSelector(result); + } + + /// + /// Produces the set union of two sequences by using the default + /// equality comparer. + /// + + public static IEnumerable Union( + this IEnumerable first, + IEnumerable second) + { + return Union(first, second, /* comparer */ null); + } + + /// + /// Produces the set union of two sequences by using a specified + /// . + /// + + public static IEnumerable Union( + this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) + { + return first.Concat(second).Distinct(comparer); + } + + /// + /// Returns the elements of the specified sequence or the type + /// parameter's default value in a singleton collection if the + /// sequence is empty. + /// + + public static IEnumerable DefaultIfEmpty( + this IEnumerable source) + { + return source.DefaultIfEmpty(default(TSource)); + } + + /// + /// Returns the elements of the specified sequence or the specified + /// value in a singleton collection if the sequence is empty. + /// + + public static IEnumerable DefaultIfEmpty( + this IEnumerable source, + TSource defaultValue) + { + CheckNotNull(source, "source"); + + return DefaultIfEmptyYield(source, defaultValue); + } + + private static IEnumerable DefaultIfEmptyYield( + IEnumerable source, + TSource defaultValue) + { + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + yield return defaultValue; + else + do + { + yield return e.Current; + } while (e.MoveNext()); + } + } + + /// + /// Determines whether all elements of a sequence satisfy a condition. + /// + + public static bool All( + this IEnumerable source, + Func predicate) + { + CheckNotNull(source, "source"); + CheckNotNull(predicate, "predicate"); + + foreach (var item in source) + if (!predicate(item)) + return false; + + return true; + } + + /// + /// Determines whether a sequence contains any elements. + /// + + public static bool Any( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + using (var e = source.GetEnumerator()) + return e.MoveNext(); + } + + /// + /// Determines whether any element of a sequence satisfies a + /// condition. + /// + + public static bool Any( + this IEnumerable source, + Func predicate) + { + return source.Where(predicate).Any(); + } + + /// + /// Determines whether a sequence contains a specified element by + /// using the default equality comparer. + /// + + public static bool Contains( + this IEnumerable source, + TSource value) + { + return source.Contains(value, /* comparer */ null); + } + + /// + /// Determines whether a sequence contains a specified element by + /// using a specified . + /// + + public static bool Contains( + this IEnumerable source, + TSource value, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + + if (comparer == null) + { + var collection = source as ICollection; + if (collection != null) + return collection.Contains(value); + } + + comparer = comparer ?? EqualityComparer.Default; + return source.Any(item => comparer.Equals(item, value)); + } + + /// + /// Determines whether two sequences are equal by comparing the + /// elements by using the default equality comparer for their type. + /// + + public static bool SequenceEqual( + this IEnumerable first, + IEnumerable second) + { + return first.SequenceEqual(second, /* comparer */ null); + } + + /// + /// Determines whether two sequences are equal by comparing their + /// elements by using a specified . + /// + + public static bool SequenceEqual( + this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) + { + CheckNotNull(first, "frist"); + CheckNotNull(second, "second"); + + comparer = comparer ?? EqualityComparer.Default; + + using (IEnumerator lhs = first.GetEnumerator(), + rhs = second.GetEnumerator()) + { + do + { + if (!lhs.MoveNext()) + return !rhs.MoveNext(); + + if (!rhs.MoveNext()) + return false; + } while (comparer.Equals(lhs.Current, rhs.Current)); + } + + return false; + } + + /// + /// Base implementation for Min/Max operator. + /// + + private static TSource MinMaxImpl( + this IEnumerable source, + Func lesser) + { + CheckNotNull(source, "source"); + Debug.Assert(lesser != null); + + return source.Aggregate((a, item) => lesser(a, item) ? a : item); + } + + /// + /// Base implementation for Min/Max operator for nullable types. + /// + + private static TSource? MinMaxImpl( + this IEnumerable source, + TSource? seed, Func lesser) where TSource : struct + { + CheckNotNull(source, "source"); + Debug.Assert(lesser != null); + + return source.Aggregate(seed, (a, item) => lesser(a, item) ? a : item); + // == MinMaxImpl(Repeat(null, 1).Concat(source), lesser); + } + + /// + /// Returns the minimum value in a generic sequence. + /// + + public static TSource Min( + this IEnumerable source) + { + var comparer = Comparer.Default; + return source.MinMaxImpl((x, y) => comparer.Compare(x, y) < 0); + } + + /// + /// Invokes a transform function on each element of a generic + /// sequence and returns the minimum resulting value. + /// + + public static TResult Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a generic sequence. + /// + + public static TSource Max( + this IEnumerable source) + { + var comparer = Comparer.Default; + return source.MinMaxImpl((x, y) => comparer.Compare(x, y) > 0); + } + + /// + /// Invokes a transform function on each element of a generic + /// sequence and returns the maximum resulting value. + /// + + public static TResult Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + + /// + /// Makes an enumerator seen as enumerable once more. + /// + /// + /// The supplied enumerator must have been started. The first element + /// returned is the element the enumerator was on when passed in. + /// DO NOT use this method if the caller must be a generator. It is + /// mostly safe among aggregate operations. + /// + + private static IEnumerable Renumerable(this IEnumerator e) + { + Debug.Assert(e != null); + + do + { + yield return e.Current; + } while (e.MoveNext()); + } + + /// + /// Sorts the elements of a sequence in ascending order according to a key. + /// + + public static IOrderedEnumerable OrderBy( + this IEnumerable source, + Func keySelector) + { + return source.OrderBy(keySelector, /* comparer */ null); + } + + /// + /// Sorts the elements of a sequence in ascending order by using a + /// specified comparer. + /// + + public static IOrderedEnumerable OrderBy( + this IEnumerable source, + Func keySelector, + IComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + + return new OrderedEnumerable(source, keySelector, comparer, /* descending */ false); + } + + /// + /// Sorts the elements of a sequence in descending order according to a key. + /// + + public static IOrderedEnumerable OrderByDescending( + this IEnumerable source, + Func keySelector) + { + return source.OrderByDescending(keySelector, /* comparer */ null); + } + + /// + /// Sorts the elements of a sequence in descending order by using a + /// specified comparer. + /// + + public static IOrderedEnumerable OrderByDescending( + this IEnumerable source, + Func keySelector, + IComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(source, "keySelector"); + + return new OrderedEnumerable(source, keySelector, comparer, /* descending */ true); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in + /// ascending order according to a key. + /// + + public static IOrderedEnumerable ThenBy( + this IOrderedEnumerable source, + Func keySelector) + { + return source.ThenBy(keySelector, /* comparer */ null); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in + /// ascending order by using a specified comparer. + /// + + public static IOrderedEnumerable ThenBy( + this IOrderedEnumerable source, + Func keySelector, + IComparer comparer) + { + CheckNotNull(source, "source"); + + return source.CreateOrderedEnumerable(keySelector, comparer, /* descending */ false); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in + /// descending order, according to a key. + /// + + public static IOrderedEnumerable ThenByDescending( + this IOrderedEnumerable source, + Func keySelector) + { + return source.ThenByDescending(keySelector, /* comparer */ null); + } + + /// + /// Performs a subsequent ordering of the elements in a sequence in + /// descending order by using a specified comparer. + /// + + public static IOrderedEnumerable ThenByDescending( + this IOrderedEnumerable source, + Func keySelector, + IComparer comparer) + { + CheckNotNull(source, "source"); + + return source.CreateOrderedEnumerable(keySelector, comparer, /* descending */ true); + } + + /// + /// Base implementation for Intersect and Except operators. + /// + + private static IEnumerable IntersectExceptImpl( + this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer, + bool flag) + { + CheckNotNull(first, "first"); + CheckNotNull(second, "second"); + + var keys = new List(); + var flags = new Dictionary(comparer); + + foreach (var item in first.Where(k => !flags.ContainsKey(k))) + { + flags.Add(item, !flag); + keys.Add(item); + } + + foreach (var item in second.Where(flags.ContainsKey)) + flags[item] = flag; + + // + // As per docs, "the marked elements are yielded in the order in + // which they were collected. + // + + return keys.Where(item => flags[item]); + } + + /// + /// Produces the set intersection of two sequences by using the + /// default equality comparer to compare values. + /// + + public static IEnumerable Intersect( + this IEnumerable first, + IEnumerable second) + { + return first.Intersect(second, /* comparer */ null); + } + + /// + /// Produces the set intersection of two sequences by using the + /// specified to compare values. + /// + + public static IEnumerable Intersect( + this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) + { + return IntersectExceptImpl(first, second, comparer, /* flag */ true); + } + + /// + /// Produces the set difference of two sequences by using the + /// default equality comparer to compare values. + /// + + public static IEnumerable Except( + this IEnumerable first, + IEnumerable second) + { + return first.Except(second, /* comparer */ null); + } + + /// + /// Produces the set difference of two sequences by using the + /// specified to compare values. + /// + + public static IEnumerable Except( + this IEnumerable first, + IEnumerable second, + IEqualityComparer comparer) + { + return IntersectExceptImpl(first, second, comparer, /* flag */ false); + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function. + /// + + public static Dictionary ToDictionary( + this IEnumerable source, + Func keySelector) + { + return source.ToDictionary(keySelector, /* comparer */ null); + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function and key comparer. + /// + + public static Dictionary ToDictionary( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return source.ToDictionary(keySelector, e => e); + } + + /// + /// Creates a from an + /// according to specified key + /// selector and element selector functions. + /// + + public static Dictionary ToDictionary( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return source.ToDictionary(keySelector, elementSelector, /* comparer */ null); + } + + /// + /// Creates a from an + /// according to a specified key + /// selector function, a comparer, and an element selector function. + /// + + public static Dictionary ToDictionary( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + CheckNotNull(source, "source"); + CheckNotNull(keySelector, "keySelector"); + CheckNotNull(elementSelector, "elementSelector"); + + var dict = new Dictionary(comparer); + + foreach (var item in source) + { + // + // ToDictionary is meant to throw ArgumentNullException if + // keySelector produces a key that is null and + // Argument exception if keySelector produces duplicate keys + // for two elements. Incidentally, the doucmentation for + // IDictionary.Add says that the Add method + // throws the same exceptions under the same circumstances + // so we don't need to do any additional checking or work + // here and let the Add implementation do all the heavy + // lifting. + // + + dict.Add(keySelector(item), elementSelector(item)); + } + + return dict; + } + + /// + /// Correlates the elements of two sequences based on matching keys. + /// The default equality comparer is used to compare keys. + /// + + public static IEnumerable Join( + this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func resultSelector) + { + return outer.Join(inner, outerKeySelector, innerKeySelector, resultSelector, /* comparer */ null); + } + + /// + /// Correlates the elements of two sequences based on matching keys. + /// The default equality comparer is used to compare keys. A + /// specified is used to compare keys. + /// + + public static IEnumerable Join( + this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func resultSelector, + IEqualityComparer comparer) + { + CheckNotNull(outer, "outer"); + CheckNotNull(inner, "inner"); + CheckNotNull(outerKeySelector, "outerKeySelector"); + CheckNotNull(innerKeySelector, "innerKeySelector"); + CheckNotNull(resultSelector, "resultSelector"); + + var lookup = inner.ToLookup(innerKeySelector, comparer); + + return + from o in outer + from i in lookup[outerKeySelector(o)] + select resultSelector(o, i); + } + + /// + /// Correlates the elements of two sequences based on equality of + /// keys and groups the results. The default equality comparer is + /// used to compare keys. + /// + + public static IEnumerable GroupJoin( + this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func, TResult> resultSelector) + { + return outer.GroupJoin(inner, outerKeySelector, innerKeySelector, resultSelector, /* comparer */ null); + } + + /// + /// Correlates the elements of two sequences based on equality of + /// keys and groups the results. The default equality comparer is + /// used to compare keys. A specified + /// is used to compare keys. + /// + + public static IEnumerable GroupJoin( + this IEnumerable outer, + IEnumerable inner, + Func outerKeySelector, + Func innerKeySelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + CheckNotNull(outer, "outer"); + CheckNotNull(inner, "inner"); + CheckNotNull(outerKeySelector, "outerKeySelector"); + CheckNotNull(innerKeySelector, "innerKeySelector"); + CheckNotNull(resultSelector, "resultSelector"); + + var lookup = inner.ToLookup(innerKeySelector, comparer); + return outer.Select(o => resultSelector(o, lookup[outerKeySelector(o)])); + } + + [DebuggerStepThrough] + private static void CheckNotNull(T value, string name) where T : class + { + if (value == null) + throw new ArgumentNullException(name); + } + + private static class Sequence + { + public static readonly IEnumerable Empty = new T[0]; + } + + private sealed class Grouping : List, IGrouping + { + internal Grouping(K key) + { + Key = key; + } + + public K Key { get; private set; } + } + } + + internal partial class Enumerable + { + /// + /// Computes the sum of a sequence of nullable values. + /// + + public static int Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + int sum = 0; + foreach (var num in source) + sum = checked(sum + num); + + return sum; + } + + /// + /// Computes the sum of a sequence of nullable + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static int Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of nullable values. + /// + + public static double Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + long count = 0; + + foreach (var num in source) + checked + { + sum += (int) num; + count++; + } + + if (count == 0) + throw new InvalidOperationException(); + + return (double) sum/count; + } + + /// + /// Computes the average of a sequence of nullable values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + + /// + /// Computes the sum of a sequence of values. + /// + + public static int? Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + int sum = 0; + foreach (var num in source) + sum = checked(sum + (num ?? 0)); + + return sum; + } + + /// + /// Computes the sum of a sequence of + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static int? Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of values. + /// + + public static double? Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + long count = 0; + + foreach (var num in source.Where(n => n != null)) + checked + { + sum += (int) num; + count++; + } + + if (count == 0) + return null; + + return (double?) sum/count; + } + + /// + /// Computes the average of a sequence of values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double? Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + /// + /// Returns the minimum value in a sequence of nullable + /// values. + /// + + public static int? Min( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), null, (min, x) => min < x); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the minimum nullable value. + /// + + public static int? Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a sequence of nullable + /// values. + /// + + public static int? Max( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), + null, (max, x) => x == null || (max != null && x.Value < max.Value)); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the maximum nullable value. + /// + + public static int? Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + + /// + /// Computes the sum of a sequence of nullable values. + /// + + public static long Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + foreach (var num in source) + sum = checked(sum + num); + + return sum; + } + + /// + /// Computes the sum of a sequence of nullable + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static long Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of nullable values. + /// + + public static double Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + long count = 0; + + foreach (var num in source) + checked + { + sum += (long) num; + count++; + } + + if (count == 0) + throw new InvalidOperationException(); + + return (double) sum/count; + } + + /// + /// Computes the average of a sequence of nullable values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + + /// + /// Computes the sum of a sequence of values. + /// + + public static long? Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + foreach (var num in source) + sum = checked(sum + (num ?? 0)); + + return sum; + } + + /// + /// Computes the sum of a sequence of + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static long? Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of values. + /// + + public static double? Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + long sum = 0; + long count = 0; + + foreach (var num in source.Where(n => n != null)) + checked + { + sum += (long) num; + count++; + } + + if (count == 0) + return null; + + return (double?) sum/count; + } + + /// + /// Computes the average of a sequence of values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double? Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + /// + /// Returns the minimum value in a sequence of nullable + /// values. + /// + + public static long? Min( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), null, (min, x) => min < x); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the minimum nullable value. + /// + + public static long? Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a sequence of nullable + /// values. + /// + + public static long? Max( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), + null, (max, x) => x == null || (max != null && x.Value < max.Value)); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the maximum nullable value. + /// + + public static long? Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + + /// + /// Computes the sum of a sequence of nullable values. + /// + + public static float Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + float sum = 0; + foreach (var num in source) + sum = checked(sum + num); + + return sum; + } + + /// + /// Computes the sum of a sequence of nullable + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static float Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of nullable values. + /// + + public static float Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + float sum = 0; + long count = 0; + + foreach (var num in source) + checked + { + sum += (float) num; + count++; + } + + if (count == 0) + throw new InvalidOperationException(); + + return (float) sum/count; + } + + /// + /// Computes the average of a sequence of nullable values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static float Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + + /// + /// Computes the sum of a sequence of values. + /// + + public static float? Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + float sum = 0; + foreach (var num in source) + sum = checked(sum + (num ?? 0)); + + return sum; + } + + /// + /// Computes the sum of a sequence of + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static float? Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of values. + /// + + public static float? Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + float sum = 0; + long count = 0; + + foreach (var num in source.Where(n => n != null)) + checked + { + sum += (float) num; + count++; + } + + if (count == 0) + return null; + + return (float?) sum/count; + } + + /// + /// Computes the average of a sequence of values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static float? Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + /// + /// Returns the minimum value in a sequence of nullable + /// values. + /// + + public static float? Min( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), null, (min, x) => min < x); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the minimum nullable value. + /// + + public static float? Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a sequence of nullable + /// values. + /// + + public static float? Max( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), + null, (max, x) => x == null || (max != null && x.Value < max.Value)); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the maximum nullable value. + /// + + public static float? Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + + /// + /// Computes the sum of a sequence of nullable values. + /// + + public static double Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + double sum = 0; + foreach (var num in source) + sum = checked(sum + num); + + return sum; + } + + /// + /// Computes the sum of a sequence of nullable + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static double Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of nullable values. + /// + + public static double Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + double sum = 0; + long count = 0; + + foreach (var num in source) + checked + { + sum += (double) num; + count++; + } + + if (count == 0) + throw new InvalidOperationException(); + + return (double) sum/count; + } + + /// + /// Computes the average of a sequence of nullable values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + + /// + /// Computes the sum of a sequence of values. + /// + + public static double? Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + double sum = 0; + foreach (var num in source) + sum = checked(sum + (num ?? 0)); + + return sum; + } + + /// + /// Computes the sum of a sequence of + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static double? Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of values. + /// + + public static double? Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + double sum = 0; + long count = 0; + + foreach (var num in source.Where(n => n != null)) + checked + { + sum += (double) num; + count++; + } + + if (count == 0) + return null; + + return (double?) sum/count; + } + + /// + /// Computes the average of a sequence of values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static double? Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + /// + /// Returns the minimum value in a sequence of nullable + /// values. + /// + + public static double? Min( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), null, (min, x) => min < x); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the minimum nullable value. + /// + + public static double? Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a sequence of nullable + /// values. + /// + + public static double? Max( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), + null, (max, x) => x == null || (max != null && x.Value < max.Value)); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the maximum nullable value. + /// + + public static double? Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + + /// + /// Computes the sum of a sequence of nullable values. + /// + + public static decimal Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + decimal sum = 0; + foreach (var num in source) + sum = checked(sum + num); + + return sum; + } + + /// + /// Computes the sum of a sequence of nullable + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static decimal Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of nullable values. + /// + + public static decimal Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + decimal sum = 0; + long count = 0; + + foreach (var num in source) + checked + { + sum += (decimal) num; + count++; + } + + if (count == 0) + throw new InvalidOperationException(); + + return (decimal) sum/count; + } + + /// + /// Computes the average of a sequence of nullable values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static decimal Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + + /// + /// Computes the sum of a sequence of values. + /// + + public static decimal? Sum( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + decimal sum = 0; + foreach (var num in source) + sum = checked(sum + (num ?? 0)); + + return sum; + } + + /// + /// Computes the sum of a sequence of + /// values that are obtained by invoking a transform function on + /// each element of the input sequence. + /// + + public static decimal? Sum( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Sum(); + } + + /// + /// Computes the average of a sequence of values. + /// + + public static decimal? Average( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + decimal sum = 0; + long count = 0; + + foreach (var num in source.Where(n => n != null)) + checked + { + sum += (decimal) num; + count++; + } + + if (count == 0) + return null; + + return (decimal?) sum/count; + } + + /// + /// Computes the average of a sequence of values + /// that are obtained by invoking a transform function on each + /// element of the input sequence. + /// + + public static decimal? Average( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Average(); + } + + /// + /// Returns the minimum value in a sequence of nullable + /// values. + /// + + public static decimal? Min( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), null, (min, x) => min < x); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the minimum nullable value. + /// + + public static decimal? Min( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Min(); + } + + /// + /// Returns the maximum value in a sequence of nullable + /// values. + /// + + public static decimal? Max( + this IEnumerable source) + { + CheckNotNull(source, "source"); + + return MinMaxImpl(source.Where(x => x != null), + null, (max, x) => x == null || (max != null && x.Value < max.Value)); + } + + /// + /// Invokes a transform function on each element of a sequence and + /// returns the maximum nullable value. + /// + + public static decimal? Max( + this IEnumerable source, + Func selector) + { + return source.Select(selector).Max(); + } + } + + /// + /// Represents a collection of objects that have a common key. + /// + internal partial interface IGrouping : IEnumerable + { + /// + /// Gets the key of the . + /// + + TKey Key { get; } + } + + /// + /// Defines an indexer, size property, and Boolean search method for + /// data structures that map keys to + /// sequences of values. + /// + internal partial interface ILookup : IEnumerable> + { + bool Contains(TKey key); + int Count { get; } + IEnumerable this[TKey key] { get; } + } + + /// + /// Represents a sorted sequence. + /// + internal partial interface IOrderedEnumerable : IEnumerable + { + /// + /// Performs a subsequent ordering on the elements of an + /// according to a key. + /// + + IOrderedEnumerable CreateOrderedEnumerable( + Func keySelector, IComparer comparer, bool descending); + } + + /// + /// Represents a collection of keys each mapped to one or more values. + /// + internal sealed class Lookup : ILookup + { + private readonly Dictionary> _map; + + internal Lookup(IEqualityComparer comparer) + { + _map = new Dictionary>(comparer); + } + + internal void Add(IGrouping item) + { + _map.Add(item.Key, item); + } + + internal IEnumerable Find(TKey key) + { + IGrouping grouping; + return _map.TryGetValue(key, out grouping) ? grouping : null; + } + + /// + /// Gets the number of key/value collection pairs in the . + /// + + public int Count + { + get { return _map.Count; } + } + + /// + /// Gets the collection of values indexed by the specified key. + /// + + public IEnumerable this[TKey key] + { + get + { + IGrouping result; + return _map.TryGetValue(key, out result) ? result : Enumerable.Empty(); + } + } + + /// + /// Determines whether a specified key is in the . + /// + + public bool Contains(TKey key) + { + return _map.ContainsKey(key); + } + + /// + /// Applies a transform function to each key and its associated + /// values and returns the results. + /// + + public IEnumerable ApplyResultSelector( + Func, TResult> resultSelector) + { + if (resultSelector == null) + throw new ArgumentNullException("resultSelector"); + + foreach (var pair in _map) + yield return resultSelector(pair.Key, pair.Value); + } + + /// + /// Returns a generic enumerator that iterates through the . + /// + + public IEnumerator> GetEnumerator() + { + return _map.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal sealed class OrderedEnumerable : IOrderedEnumerable + { + private readonly IEnumerable _source; + private readonly List> _comparisons; + + public OrderedEnumerable(IEnumerable source, + Func keySelector, IComparer comparer, bool descending) : + this(source, null, keySelector, comparer, descending) + { + } + + private OrderedEnumerable(IEnumerable source, List> comparisons, + Func keySelector, IComparer comparer, bool descending) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + _source = source; + + comparer = comparer ?? Comparer.Default; + + if (comparisons == null) + comparisons = new List>( /* capacity */ 4); + + comparisons.Add((x, y) + => (descending ? -1 : 1)*comparer.Compare(keySelector(x), keySelector(y))); + + _comparisons = comparisons; + } + + public IOrderedEnumerable CreateOrderedEnumerable( + Func keySelector, IComparer comparer, bool descending) + { + return new OrderedEnumerable(_source, _comparisons, keySelector, comparer, descending); + } + + public IEnumerator GetEnumerator() + { + // + // We sort using List.Sort, but docs say that it performs an + // unstable sort. LINQ, on the other hand, says OrderBy performs + // a stable sort. So convert the source sequence into a sequence + // of tuples where the second element tags the position of the + // element from the source sequence (First). The position is + // then used as a tie breaker when all keys compare equal, + // thus making the sort stable. + // + + var list = _source.Select(new Func>(TagPosition)).ToList(); + + list.Sort((x, y) => + { + // + // Compare keys from left to right. + // + + var comparisons = _comparisons; + for (var i = 0; i < comparisons.Count; i++) + { + var result = comparisons[i](x.First, y.First); + if (result != 0) + return result; + } + + // + // All keys compared equal so now break the tie by their + // position in the original sequence, making the sort stable. + // + + return x.Second.CompareTo(y.Second); + }); + + return list.Select(new Func, T>(GetFirst)).GetEnumerator(); + + } + + /// + /// See issue #11 + /// for why this method is needed and cannot be expressed as a + /// lambda at the call site. + /// + + private static Tuple TagPosition(T e, int i) + { + return new Tuple(e, i); + } + + /// + /// See issue #11 + /// for why this method is needed and cannot be expressed as a + /// lambda at the call site. + /// + + private static T GetFirst(Tuple pv) + { + return pv.First; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + [Serializable] + internal struct Tuple : IEquatable> + { + public TFirst First { get; private set; } + public TSecond Second { get; private set; } + + public Tuple(TFirst first, TSecond second) + : this() + { + First = first; + Second = second; + } + + public override bool Equals(object obj) + { + return obj != null + && obj is Tuple + && base.Equals((Tuple) obj); + } + + public bool Equals(Tuple other) + { + return EqualityComparer.Default.Equals(other.First, First) + && EqualityComparer.Default.Equals(other.Second, Second); + } + + public override int GetHashCode() + { + var num = 0x7a2f0b42; + num = (-1521134295*num) + EqualityComparer.Default.GetHashCode(First); + return (-1521134295*num) + EqualityComparer.Default.GetHashCode(Second); + } + + public override string ToString() + { + return string.Format(CultureInfo.InvariantCulture, @"{{ First = {0}, Second = {1} }}", First, Second); + } + } +} + +namespace Newtonsoft.Json.Serialization +{ +#pragma warning disable 1591 + public delegate TResult Func(); + + public delegate TResult Func(T a); + + public delegate TResult Func(T1 arg1, T2 arg2); + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate void Action(); + + public delegate void Action(T1 arg1, T2 arg2); + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); +#pragma warning restore 1591 +} + +namespace System.Runtime.CompilerServices +{ + /// + /// This attribute allows us to define extension methods without + /// requiring .NET Framework 3.5. For more information, see the section, + /// Extension Methods in .NET Framework 2.0 Apps, + /// of Basic Instincts: Extension Methods + /// column in MSDN Magazine, + /// issue Nov 2007. + /// + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] + internal sealed class ExtensionAttribute : Attribute { } +} + +#endif \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/MathUtils.cs b/Libs/JsonNet/Utilities/MathUtils.cs new file mode 100644 index 0000000..753b4c2 --- /dev/null +++ b/Libs/JsonNet/Utilities/MathUtils.cs @@ -0,0 +1,117 @@ +#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.Utilities +{ + internal static class MathUtils + { + public static int IntLength(ulong i) + { + if (i < 10000000000) + { + if (i < 10) return 1; + if (i < 100) return 2; + if (i < 1000) return 3; + if (i < 10000) return 4; + if (i < 100000) return 5; + if (i < 1000000) return 6; + if (i < 10000000) return 7; + if (i < 100000000) return 8; + if (i < 1000000000) return 9; + + return 10; + } + else + { + if (i < 100000000000) return 11; + if (i < 1000000000000) return 12; + if (i < 10000000000000) return 13; + if (i < 100000000000000) return 14; + if (i < 1000000000000000) return 15; + if (i < 10000000000000000) return 16; + if (i < 100000000000000000) return 17; + if (i < 1000000000000000000) return 18; + if (i < 10000000000000000000) return 19; + + return 20; + } + } + + public static char IntToHex(int n) + { + if (n <= 9) + return (char)(n + 48); + + return (char)((n - 10) + 97); + } + + public static int? Min(int? val1, int? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Min(val1.Value, val2.Value); + } + + public static int? Max(int? val1, int? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Max(val1.Value, val2.Value); + } + + public static double? Max(double? val1, double? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Max(val1.Value, val2.Value); + } + + public static bool ApproxEquals(double d1, double d2) + { + const double epsilon = 2.2204460492503131E-16; + + if (d1 == d2) + return true; + + double tolerance = ((Math.Abs(d1) + Math.Abs(d2)) + 10.0) * epsilon; + double difference = d1 - d2; + + return (-tolerance < difference && tolerance > difference); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/MethodCall.cs b/Libs/JsonNet/Utilities/MethodCall.cs new file mode 100644 index 0000000..8b16e28 --- /dev/null +++ b/Libs/JsonNet/Utilities/MethodCall.cs @@ -0,0 +1,29 @@ +#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 + +namespace Newtonsoft.Json.Utilities +{ + internal delegate TResult MethodCall(T target, params object[] args); +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/MiscellaneousUtils.cs b/Libs/JsonNet/Utilities/MiscellaneousUtils.cs new file mode 100644 index 0000000..76f000b --- /dev/null +++ b/Libs/JsonNet/Utilities/MiscellaneousUtils.cs @@ -0,0 +1,140 @@ +#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; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal delegate T Creator(); + + internal static class MiscellaneousUtils + { + public static bool ValueEquals(object objA, object objB) + { + if (objA == null && objB == null) + return true; + if (objA != null && objB == null) + return false; + if (objA == null && objB != null) + return false; + + // comparing an Int32 and Int64 both of the same value returns false + // make types the same then compare + if (objA.GetType() != objB.GetType()) + { + if (ConvertUtils.IsInteger(objA) && ConvertUtils.IsInteger(objB)) + return Convert.ToDecimal(objA, CultureInfo.CurrentCulture).Equals(Convert.ToDecimal(objB, CultureInfo.CurrentCulture)); + else if ((objA is double || objA is float || objA is decimal) && (objB is double || objB is float || objB is decimal)) + return MathUtils.ApproxEquals(Convert.ToDouble(objA, CultureInfo.CurrentCulture), Convert.ToDouble(objB, CultureInfo.CurrentCulture)); + else + return false; + } + + return objA.Equals(objB); + } + + public static ArgumentOutOfRangeException CreateArgumentOutOfRangeException(string paramName, object actualValue, string message) + { + string newMessage = message + Environment.NewLine + @"Actual value was {0}.".FormatWith(CultureInfo.InvariantCulture, actualValue); + + return new ArgumentOutOfRangeException(paramName, newMessage); + } + + public static string ToString(object value) + { + if (value == null) + return "{null}"; + + return (value is string) ? @"""" + value.ToString() + @"""" : value.ToString(); + } + + public static int ByteArrayCompare(byte[] a1, byte[] a2) + { + int lengthCompare = a1.Length.CompareTo(a2.Length); + if (lengthCompare != 0) + return lengthCompare; + + for (int i = 0; i < a1.Length; i++) + { + int valueCompare = a1[i].CompareTo(a2[i]); + if (valueCompare != 0) + return valueCompare; + } + + return 0; + } + + public static string GetPrefix(string qualifiedName) + { + string prefix; + string localName; + GetQualifiedNameParts(qualifiedName, out prefix, out localName); + + return prefix; + } + + public static string GetLocalName(string qualifiedName) + { + string prefix; + string localName; + GetQualifiedNameParts(qualifiedName, out prefix, out localName); + + return localName; + } + + public static void GetQualifiedNameParts(string qualifiedName, out string prefix, out string localName) + { + int colonPosition = qualifiedName.IndexOf(':'); + + if ((colonPosition == -1 || colonPosition == 0) || (qualifiedName.Length - 1) == colonPosition) + { + prefix = null; + localName = qualifiedName; + } + else + { + prefix = qualifiedName.Substring(0, colonPosition); + localName = qualifiedName.Substring(colonPosition + 1); + } + } + + internal static string FormatValueForPrint(object value) + { + if (value == null) + return "{null}"; + + if (value is string) + return @"""" + value + @""""; + + return value.ToString(); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/PropertyNameTable.cs b/Libs/JsonNet/Utilities/PropertyNameTable.cs new file mode 100644 index 0000000..fd593a2 --- /dev/null +++ b/Libs/JsonNet/Utilities/PropertyNameTable.cs @@ -0,0 +1,160 @@ +#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; + +namespace Newtonsoft.Json.Utilities +{ + internal class PropertyNameTable + { + // used to defeat hashtable DoS attack where someone passes in lots of strings that hash to the same hash code + private static readonly int HashCodeRandomizer; + + private int _count; + private Entry[] _entries; + private int _mask = 31; + + static PropertyNameTable() + { + HashCodeRandomizer = Environment.TickCount; + } + + public PropertyNameTable() + { + _entries = new Entry[_mask + 1]; + } + + public string Get(char[] key, int start, int length) + { + if (length == 0) + return string.Empty; + + int hashCode = length + HashCodeRandomizer; + hashCode += (hashCode << 7) ^ key[start]; + int end = start + length; + for (int i = start + 1; i < end; i++) + { + hashCode += (hashCode << 7) ^ key[i]; + } + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next) + { + if (entry.HashCode == hashCode && TextEquals(entry.Value, key, start, length)) + return entry.Value; + } + + return null; + } + + public string Add(string key) + { + if (key == null) + throw new ArgumentNullException("key"); + + int length = key.Length; + if (length == 0) + return string.Empty; + + int hashCode = length + HashCodeRandomizer; + for (int i = 0; i < key.Length; i++) + { + hashCode += (hashCode << 7) ^ key[i]; + } + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + for (Entry entry = _entries[hashCode & _mask]; entry != null; entry = entry.Next) + { + if (entry.HashCode == hashCode && entry.Value.Equals(key)) + return entry.Value; + } + + return AddEntry(key, hashCode); + } + + + private string AddEntry(string str, int hashCode) + { + int index = hashCode & _mask; + Entry entry = new Entry(str, hashCode, _entries[index]); + _entries[index] = entry; + if (_count++ == _mask) + { + Grow(); + } + return entry.Value; + } + + private void Grow() + { + Entry[] entries = _entries; + int newMask = (_mask * 2) + 1; + Entry[] newEntries = new Entry[newMask + 1]; + + for (int i = 0; i < entries.Length; i++) + { + Entry next; + for (Entry entry = entries[i]; entry != null; entry = next) + { + int index = entry.HashCode & newMask; + next = entry.Next; + entry.Next = newEntries[index]; + newEntries[index] = entry; + } + } + _entries = newEntries; + _mask = newMask; + } + + private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length) + { + if (str1.Length != str2Length) + return false; + + for (int i = 0; i < str1.Length; i++) + { + if (str1[i] != str2[str2Start + i]) + return false; + } + return true; + } + + private class Entry + { + internal readonly string Value; + internal readonly int HashCode; + internal Entry Next; + + internal Entry(string value, int hashCode, Entry next) + { + Value = value; + HashCode = hashCode; + Next = next; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ReflectionDelegateFactory.cs b/Libs/JsonNet/Utilities/ReflectionDelegateFactory.cs new file mode 100644 index 0000000..1d9ffc5 --- /dev/null +++ b/Libs/JsonNet/Utilities/ReflectionDelegateFactory.cs @@ -0,0 +1,73 @@ +#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.Globalization; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal abstract class ReflectionDelegateFactory + { + public Func CreateGet(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return CreateGet(propertyInfo); + + FieldInfo fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return CreateGet(fieldInfo); + + throw new Exception("Could not create getter for {0}.".FormatWith(CultureInfo.InvariantCulture, memberInfo)); + } + + public Action CreateSet(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return CreateSet(propertyInfo); + + FieldInfo fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return CreateSet(fieldInfo); + + throw new Exception("Could not create setter for {0}.".FormatWith(CultureInfo.InvariantCulture, memberInfo)); + } + + public abstract MethodCall CreateMethodCall(MethodBase method); + public abstract ObjectConstructor CreateParametrizedConstructor(MethodBase method); + public abstract Func CreateDefaultConstructor(Type type); + public abstract Func CreateGet(PropertyInfo propertyInfo); + public abstract Func CreateGet(FieldInfo fieldInfo); + public abstract Action CreateSet(FieldInfo fieldInfo); + public abstract Action CreateSet(PropertyInfo propertyInfo); + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ReflectionObject.cs b/Libs/JsonNet/Utilities/ReflectionObject.cs new file mode 100644 index 0000000..7da01e0 --- /dev/null +++ b/Libs/JsonNet/Utilities/ReflectionObject.cs @@ -0,0 +1,155 @@ +#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 Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Resources; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal class ReflectionMember + { + public Type MemberType { get; set; } + public Func Getter { get; set; } + public Action Setter { get; set; } + } + + internal class ReflectionObject + { + public ObjectConstructor Creator { get; private set; } + public IDictionary Members { get; private set; } + + public ReflectionObject() + { + Members = new Dictionary(); + } + + public object GetValue(object target, string member) + { + Func getter = Members[member].Getter; + return getter(target); + } + + public void SetValue(object target, string member, object value) + { + Action setter = Members[member].Setter; + setter(target, value); + } + + public Type GetType(string member) + { + return Members[member].MemberType; + } + + public static ReflectionObject Create(Type t, params string[] memberNames) + { + return Create(t, null, memberNames); + } + + public static ReflectionObject Create(Type t, MethodBase creator, params string[] memberNames) + { + ReflectionObject d = new ReflectionObject(); + + ReflectionDelegateFactory delegateFactory = JsonTypeReflector.ReflectionDelegateFactory; + + if (creator != null) + { + d.Creator = delegateFactory.CreateParametrizedConstructor(creator); + } + else + { + if (ReflectionUtils.HasDefaultConstructor(t, false)) + { + Func ctor = delegateFactory.CreateDefaultConstructor(t); + + d.Creator = args => ctor(); + } + } + + foreach (string memberName in memberNames) + { + MemberInfo[] members = t.GetMember(memberName, BindingFlags.Instance | BindingFlags.Public); + if (members.Length != 1) + throw new ArgumentException("Expected a single member with the name '{0}'.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + MemberInfo member = members.Single(); + + ReflectionMember reflectionMember = new ReflectionMember(); + + switch (member.MemberType()) + { + case MemberTypes.Field: + case MemberTypes.Property: + if (ReflectionUtils.CanReadMemberValue(member, false)) + reflectionMember.Getter = delegateFactory.CreateGet(member); + + if (ReflectionUtils.CanSetMemberValue(member, false, false)) + reflectionMember.Setter = delegateFactory.CreateSet(member); + break; + case MemberTypes.Method: + MethodInfo method = (MethodInfo)member; + if (method.IsPublic) + { + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == 0 && method.ReturnType != typeof(void)) + { + MethodCall call = delegateFactory.CreateMethodCall(method); + reflectionMember.Getter = target => call(target); + } + else if (parameters.Length == 1 && method.ReturnType == typeof(void)) + { + MethodCall call = delegateFactory.CreateMethodCall(method); + reflectionMember.Setter = (target, arg) => call(target, arg); + } + } + break; + default: + throw new ArgumentException("Unexpected member type '{0}' for member '{1}'.".FormatWith(CultureInfo.InvariantCulture, member.MemberType(), member.Name)); + } + + if (ReflectionUtils.CanReadMemberValue(member, false)) + reflectionMember.Getter = delegateFactory.CreateGet(member); + + if (ReflectionUtils.CanSetMemberValue(member, false, false)) + reflectionMember.Setter = delegateFactory.CreateSet(member); + + reflectionMember.MemberType = ReflectionUtils.GetMemberUnderlyingType(member); + + d.Members[memberName] = reflectionMember; + } + + return d; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ReflectionUtils.cs b/Libs/JsonNet/Utilities/ReflectionUtils.cs new file mode 100644 index 0000000..0c3ef8c --- /dev/null +++ b/Libs/JsonNet/Utilities/ReflectionUtils.cs @@ -0,0 +1,978 @@ +#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; +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) +using System.Numerics; +#endif +using System.Reflection; +using System.Collections; +using System.Globalization; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Text; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ +#if (NETFX_CORE || PORTABLE || PORTABLE40) + internal enum MemberTypes + { + Property, + Field, + Event, + Method, + Other + } +#endif + +#if NETFX_CORE || PORTABLE + [Flags] + internal enum BindingFlags + { + Default = 0, + IgnoreCase = 1, + DeclaredOnly = 2, + Instance = 4, + Static = 8, + Public = 16, + NonPublic = 32, + FlattenHierarchy = 64, + InvokeMethod = 256, + CreateInstance = 512, + GetField = 1024, + SetField = 2048, + GetProperty = 4096, + SetProperty = 8192, + PutDispProperty = 16384, + ExactBinding = 65536, + PutRefDispProperty = 32768, + SuppressChangeType = 131072, + OptionalParamBinding = 262144, + IgnoreReturn = 16777216 + } +#endif + + public static class ReflectionUtils + { + public static readonly Type[] EmptyTypes; + + static ReflectionUtils() + { +#if !(NETFX_CORE || PORTABLE40 || PORTABLE) + EmptyTypes = Type.EmptyTypes; +#else + EmptyTypes = new Type[0]; +#endif + } + + public static bool IsVirtual(this PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + MethodInfo m = propertyInfo.GetGetMethod(); + if (m != null && m.IsVirtual) + return true; + + m = propertyInfo.GetSetMethod(); + if (m != null && m.IsVirtual) + return true; + + return false; + } + + public static MethodInfo GetBaseDefinition(this PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + MethodInfo m = propertyInfo.GetGetMethod(); + if (m != null) + return m.GetBaseDefinition(); + + m = propertyInfo.GetSetMethod(); + if (m != null) + return m.GetBaseDefinition(); + + return null; + } + + public static bool IsPublic(PropertyInfo property) + { + if (property.GetGetMethod() != null && property.GetGetMethod().IsPublic) + return true; + if (property.GetSetMethod() != null && property.GetSetMethod().IsPublic) + return true; + + return false; + } + + public static Type GetObjectType(object v) + { + return (v != null) ? v.GetType() : null; + } + + public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat, SerializationBinder binder) + { + string fullyQualifiedTypeName; +#if !(NET20 || NET35) + if (binder != null) + { + string assemblyName, typeName; + binder.BindToName(t, out assemblyName, out typeName); + fullyQualifiedTypeName = typeName + (assemblyName == null ? "" : ", " + assemblyName); + } + else + { + fullyQualifiedTypeName = t.AssemblyQualifiedName; + } +#else + fullyQualifiedTypeName = t.AssemblyQualifiedName; +#endif + + switch (assemblyFormat) + { + case FormatterAssemblyStyle.Simple: + return RemoveAssemblyDetails(fullyQualifiedTypeName); + case FormatterAssemblyStyle.Full: + return fullyQualifiedTypeName; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private static string RemoveAssemblyDetails(string fullyQualifiedTypeName) + { + StringBuilder builder = new StringBuilder(); + + // loop through the type name and filter out qualified assembly details from nested type names + bool writingAssemblyName = false; + bool skippingAssemblyDetails = false; + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) + { + char current = fullyQualifiedTypeName[i]; + switch (current) + { + case '[': + writingAssemblyName = false; + skippingAssemblyDetails = false; + builder.Append(current); + break; + case ']': + writingAssemblyName = false; + skippingAssemblyDetails = false; + builder.Append(current); + break; + case ',': + if (!writingAssemblyName) + { + writingAssemblyName = true; + builder.Append(current); + } + else + { + skippingAssemblyDetails = true; + } + break; + default: + if (!skippingAssemblyDetails) + builder.Append(current); + break; + } + } + + return builder.ToString(); + } + + public static bool HasDefaultConstructor(Type t, bool nonPublic) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + if (t.IsValueType()) + return true; + + return (GetDefaultConstructor(t, nonPublic) != null); + } + + public static ConstructorInfo GetDefaultConstructor(Type t) + { + return GetDefaultConstructor(t, false); + } + + public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic) + { + BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; + if (nonPublic) + bindingFlags = bindingFlags | BindingFlags.NonPublic; + + return t.GetConstructors(bindingFlags).SingleOrDefault(c => !c.GetParameters().Any()); + } + + public static bool IsNullable(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + if (t.IsValueType()) + return IsNullableType(t); + + return true; + } + + public static bool IsNullableType(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + return (t.IsGenericType() && t.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + public static Type EnsureNotNullableType(Type t) + { + return (IsNullableType(t)) + ? Nullable.GetUnderlyingType(t) + : t; + } + + public static bool IsGenericDefinition(Type type, Type genericInterfaceDefinition) + { + if (!type.IsGenericType()) + return false; + + Type t = type.GetGenericTypeDefinition(); + return (t == genericInterfaceDefinition); + } + + public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition) + { + Type implementingType; + return ImplementsGenericDefinition(type, genericInterfaceDefinition, out implementingType); + } + + public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition, out Type implementingType) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, "genericInterfaceDefinition"); + + if (!genericInterfaceDefinition.IsInterface() || !genericInterfaceDefinition.IsGenericTypeDefinition()) + throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition)); + + if (type.IsInterface()) + { + if (type.IsGenericType()) + { + Type interfaceDefinition = type.GetGenericTypeDefinition(); + + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = type; + return true; + } + } + } + + foreach (Type i in type.GetInterfaces()) + { + if (i.IsGenericType()) + { + Type interfaceDefinition = i.GetGenericTypeDefinition(); + + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = i; + return true; + } + } + } + + implementingType = null; + return false; + } + + public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition) + { + Type implementingType; + return InheritsGenericDefinition(type, genericClassDefinition, out implementingType); + } + + public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition, out Type implementingType) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(genericClassDefinition, "genericClassDefinition"); + + if (!genericClassDefinition.IsClass() || !genericClassDefinition.IsGenericTypeDefinition()) + throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition)); + + return InheritsGenericDefinitionInternal(type, genericClassDefinition, out implementingType); + } + + private static bool InheritsGenericDefinitionInternal(Type currentType, Type genericClassDefinition, out Type implementingType) + { + if (currentType.IsGenericType()) + { + Type currentGenericClassDefinition = currentType.GetGenericTypeDefinition(); + + if (genericClassDefinition == currentGenericClassDefinition) + { + implementingType = currentType; + return true; + } + } + + if (currentType.BaseType() == null) + { + implementingType = null; + return false; + } + + return InheritsGenericDefinitionInternal(currentType.BaseType(), genericClassDefinition, out implementingType); + } + + /// + /// Gets the type of the typed collection's items. + /// + /// The type. + /// The type of the typed collection's items. + public static Type GetCollectionItemType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + Type genericListType; + + if (type.IsArray) + { + return type.GetElementType(); + } + if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out genericListType)) + { + if (genericListType.IsGenericTypeDefinition()) + throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); + + return genericListType.GetGenericArguments()[0]; + } + if (typeof(IEnumerable).IsAssignableFrom(type)) + { + return null; + } + + throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); + } + + public static void GetDictionaryKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) + { + ValidationUtils.ArgumentNotNull(dictionaryType, "type"); + + Type genericDictionaryType; + if (ImplementsGenericDefinition(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType)) + { + if (genericDictionaryType.IsGenericTypeDefinition()) + throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); + + Type[] dictionaryGenericArguments = genericDictionaryType.GetGenericArguments(); + + keyType = dictionaryGenericArguments[0]; + valueType = dictionaryGenericArguments[1]; + return; + } + if (typeof(IDictionary).IsAssignableFrom(dictionaryType)) + { + keyType = null; + valueType = null; + return; + } + + throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); + } + + /// + /// Gets the member's underlying type. + /// + /// The member. + /// The underlying type of the member. + public static Type GetMemberUnderlyingType(MemberInfo member) + { + ValidationUtils.ArgumentNotNull(member, "member"); + + switch (member.MemberType()) + { + case MemberTypes.Field: + return ((FieldInfo)member).FieldType; + case MemberTypes.Property: + return ((PropertyInfo)member).PropertyType; + case MemberTypes.Event: + return ((EventInfo)member).EventHandlerType; + case MemberTypes.Method: + return ((MethodInfo)member).ReturnType; + default: + throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo, EventInfo or MethodInfo", "member"); + } + } + + /// + /// Determines whether the member is an indexed property. + /// + /// The member. + /// + /// true if the member is an indexed property; otherwise, false. + /// + public static bool IsIndexedProperty(MemberInfo member) + { + ValidationUtils.ArgumentNotNull(member, "member"); + + PropertyInfo propertyInfo = member as PropertyInfo; + + if (propertyInfo != null) + return IsIndexedProperty(propertyInfo); + else + return false; + } + + /// + /// Determines whether the property is an indexed property. + /// + /// The property. + /// + /// true if the property is an indexed property; otherwise, false. + /// + public static bool IsIndexedProperty(PropertyInfo property) + { + ValidationUtils.ArgumentNotNull(property, "property"); + + return (property.GetIndexParameters().Length > 0); + } + + /// + /// Gets the member's value on the object. + /// + /// The member. + /// The target object. + /// The member's value on the object. + public static object GetMemberValue(MemberInfo member, object target) + { + ValidationUtils.ArgumentNotNull(member, "member"); + ValidationUtils.ArgumentNotNull(target, "target"); + + switch (member.MemberType()) + { + case MemberTypes.Field: + return ((FieldInfo)member).GetValue(target); + case MemberTypes.Property: + try + { + return ((PropertyInfo)member).GetValue(target, null); + } + catch (TargetParameterCountException e) + { + throw new ArgumentException("MemberInfo '{0}' has index parameters".FormatWith(CultureInfo.InvariantCulture, member.Name), e); + } + default: + throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, member.Name), "member"); + } + } + + /// + /// Sets the member's value on the target object. + /// + /// The member. + /// The target. + /// The value. + public static void SetMemberValue(MemberInfo member, object target, object value) + { + ValidationUtils.ArgumentNotNull(member, "member"); + ValidationUtils.ArgumentNotNull(target, "target"); + + switch (member.MemberType()) + { + case MemberTypes.Field: + ((FieldInfo)member).SetValue(target, value); + break; + case MemberTypes.Property: + ((PropertyInfo)member).SetValue(target, value, null); + break; + default: + throw new ArgumentException("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), "member"); + } + } + + /// + /// Determines whether the specified MemberInfo can be read. + /// + /// The MemberInfo to determine whether can be read. + /// /// if set to true then allow the member to be gotten non-publicly. + /// + /// true if the specified MemberInfo can be read; otherwise, false. + /// + public static bool CanReadMemberValue(MemberInfo member, bool nonPublic) + { + switch (member.MemberType()) + { + case MemberTypes.Field: + FieldInfo fieldInfo = (FieldInfo)member; + + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo)member; + + if (!propertyInfo.CanRead) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetGetMethod(nonPublic) != null); + default: + return false; + } + } + + /// + /// Determines whether the specified MemberInfo can be set. + /// + /// The MemberInfo to determine whether can be set. + /// if set to true then allow the member to be set non-publicly. + /// if set to true then allow the member to be set if read-only. + /// + /// true if the specified MemberInfo can be set; otherwise, false. + /// + public static bool CanSetMemberValue(MemberInfo member, bool nonPublic, bool canSetReadOnly) + { + switch (member.MemberType()) + { + case MemberTypes.Field: + FieldInfo fieldInfo = (FieldInfo)member; + + if (fieldInfo.IsInitOnly && !canSetReadOnly) + return false; + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo)member; + + if (!propertyInfo.CanWrite) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetSetMethod(nonPublic) != null); + default: + return false; + } + } + + public static List GetFieldsAndProperties(Type type, BindingFlags bindingAttr) + { + List targetMembers = new List(); + + targetMembers.AddRange(GetFields(type, bindingAttr)); + targetMembers.AddRange(GetProperties(type, bindingAttr)); + + // for some reason .NET returns multiple members when overriding a generic member on a base class + // http://social.msdn.microsoft.com/Forums/en-US/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/reflection-overriden-abstract-generic-properties?forum=netfxbcl + // filter members to only return the override on the topmost class + // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... + List distinctMembers = new List(targetMembers.Count); + + foreach (var groupedMember in targetMembers.GroupBy(m => m.Name)) + { + int count = groupedMember.Count(); + IList members = groupedMember.ToList(); + + if (count == 1) + { + distinctMembers.Add(members.First()); + } + else + { + IList resolvedMembers = new List(); + foreach (MemberInfo memberInfo in members) + { + // this is a bit hacky + // if the hiding property is hiding a base property and it is virtual + // then this ensures the derived property gets used + if (resolvedMembers.Count == 0) + resolvedMembers.Add(memberInfo); + else if (!IsOverridenGenericMember(memberInfo, bindingAttr) || memberInfo.Name == "Item") + resolvedMembers.Add(memberInfo); + } + + distinctMembers.AddRange(resolvedMembers); + } + } + + return distinctMembers; + } + + private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr) + { + if (memberInfo.MemberType() != MemberTypes.Property) + return false; + + PropertyInfo propertyInfo = (PropertyInfo)memberInfo; + if (!IsVirtual(propertyInfo)) + return false; + + Type declaringType = propertyInfo.DeclaringType; + if (!declaringType.IsGenericType()) + return false; + Type genericTypeDefinition = declaringType.GetGenericTypeDefinition(); + if (genericTypeDefinition == null) + return false; + MemberInfo[] members = genericTypeDefinition.GetMember(propertyInfo.Name, bindingAttr); + if (members.Length == 0) + return false; + Type memberUnderlyingType = GetMemberUnderlyingType(members[0]); + if (!memberUnderlyingType.IsGenericParameter) + return false; + + return true; + } + + public static T GetAttribute(object attributeProvider) where T : Attribute + { + return GetAttribute(attributeProvider, true); + } + + public static T GetAttribute(object attributeProvider, bool inherit) where T : Attribute + { + T[] attributes = GetAttributes(attributeProvider, inherit); + + return (attributes != null) ? attributes.SingleOrDefault() : null; + } + +#if !(NETFX_CORE || PORTABLE) + public static T[] GetAttributes(object attributeProvider, bool inherit) where T : Attribute + { + return (T[])GetAttributes(attributeProvider, typeof(T), inherit); + } + + public static Attribute[] GetAttributes(object attributeProvider, Type attributeType, bool inherit) + { + ValidationUtils.ArgumentNotNull(attributeProvider, "attributeProvider"); + + object provider = attributeProvider; + + // http://hyperthink.net/blog/getcustomattributes-gotcha/ + // ICustomAttributeProvider doesn't do inheritance + + if (provider is Type) + return (Attribute[])((Type)provider).GetCustomAttributes(attributeType, inherit); + + if (provider is Assembly) + return Attribute.GetCustomAttributes((Assembly)provider, attributeType); + + if (provider is MemberInfo) + return Attribute.GetCustomAttributes((MemberInfo)provider, attributeType, inherit); + +#if !PORTABLE40 + if (provider is Module) + return Attribute.GetCustomAttributes((Module)provider, attributeType, inherit); +#endif + + if (provider is ParameterInfo) + return Attribute.GetCustomAttributes((ParameterInfo)provider, attributeType, inherit); + +#if !PORTABLE40 + return (Attribute[])((ICustomAttributeProvider)attributeProvider).GetCustomAttributes(attributeType, inherit); +#else + throw new Exception("Cannot get attributes from '{0}'.".FormatWith(CultureInfo.InvariantCulture, provider)); +#endif + } +#else + public static T[] GetAttributes(object attributeProvider, bool inherit) where T : Attribute + { + return GetAttributes(attributeProvider, typeof(T), inherit).Cast().ToArray(); + } + + public static Attribute[] GetAttributes(object provider, Type attributeType, bool inherit) + { + if (provider is Type) + return ((Type) provider).GetTypeInfo().GetCustomAttributes(attributeType, inherit).ToArray(); + + if (provider is Assembly) + return ((Assembly) provider).GetCustomAttributes(attributeType).ToArray(); + + if (provider is MemberInfo) + return ((MemberInfo) provider).GetCustomAttributes(attributeType, inherit).ToArray(); + + if (provider is Module) + return ((Module) provider).GetCustomAttributes(attributeType).ToArray(); + + if (provider is ParameterInfo) + return ((ParameterInfo) provider).GetCustomAttributes(attributeType, inherit).ToArray(); + + throw new Exception("Cannot get attributes from '{0}'.".FormatWith(CultureInfo.InvariantCulture, provider)); + } +#endif + + public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName) + { + int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); + + if (assemblyDelimiterIndex != null) + { + typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.Value).Trim(); + assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.Value + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.Value - 1).Trim(); + } + else + { + typeName = fullyQualifiedTypeName; + assemblyName = null; + } + } + + private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) + { + // we need to get the first comma following all surrounded in brackets because of generic types + // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + int scope = 0; + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) + { + char current = fullyQualifiedTypeName[i]; + switch (current) + { + case '[': + scope++; + break; + case ']': + scope--; + break; + case ',': + if (scope == 0) + return i; + break; + } + } + + return null; + } + + public static MemberInfo GetMemberInfoFromType(Type targetType, MemberInfo memberInfo) + { + const BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + switch (memberInfo.MemberType()) + { + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo)memberInfo; + + Type[] types = propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(); + + return targetType.GetProperty(propertyInfo.Name, bindingAttr, null, propertyInfo.PropertyType, types, null); + default: + return targetType.GetMember(memberInfo.Name, memberInfo.MemberType(), bindingAttr).SingleOrDefault(); + } + } + + public static IEnumerable GetFields(Type targetType, BindingFlags bindingAttr) + { + ValidationUtils.ArgumentNotNull(targetType, "targetType"); + + List fieldInfos = new List(targetType.GetFields(bindingAttr)); +#if !(NETFX_CORE || PORTABLE) + // Type.GetFields doesn't return inherited private fields + // manually find private fields from base class + GetChildPrivateFields(fieldInfos, targetType, bindingAttr); +#endif + + return fieldInfos.Cast(); + } + +#if !(NETFX_CORE || PORTABLE) + private static void GetChildPrivateFields(IList initialFields, Type targetType, BindingFlags bindingAttr) + { + // fix weirdness with private FieldInfos only being returned for the current Type + // find base type fields and add them to result + if ((bindingAttr & BindingFlags.NonPublic) != 0) + { + // modify flags to not search for public fields + BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); + + while ((targetType = targetType.BaseType()) != null) + { + // filter out protected fields + IEnumerable childPrivateFields = + targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate).Cast(); + + initialFields.AddRange(childPrivateFields); + } + } + } +#endif + + public static IEnumerable GetProperties(Type targetType, BindingFlags bindingAttr) + { + ValidationUtils.ArgumentNotNull(targetType, "targetType"); + + List propertyInfos = new List(targetType.GetProperties(bindingAttr)); + GetChildPrivateProperties(propertyInfos, targetType, bindingAttr); + + // a base class private getter/setter will be inaccessable unless the property was gotten from the base class + for (int i = 0; i < propertyInfos.Count; i++) + { + PropertyInfo member = propertyInfos[i]; + if (member.DeclaringType != targetType) + { + PropertyInfo declaredMember = (PropertyInfo)GetMemberInfoFromType(member.DeclaringType, member); + propertyInfos[i] = declaredMember; + } + } + + return propertyInfos; + } + + public static BindingFlags RemoveFlag(this BindingFlags bindingAttr, BindingFlags flag) + { + return ((bindingAttr & flag) == flag) + ? bindingAttr ^ flag + : bindingAttr; + } + + private static void GetChildPrivateProperties(IList initialProperties, Type targetType, BindingFlags bindingAttr) + { + // fix weirdness with private PropertyInfos only being returned for the current Type + // find base type properties and add them to result + + // also find base properties that have been hidden by subtype properties with the same name + + while ((targetType = targetType.BaseType()) != null) + { + foreach (PropertyInfo propertyInfo in targetType.GetProperties(bindingAttr)) + { + PropertyInfo subTypeProperty = propertyInfo; + + if (!IsPublic(subTypeProperty)) + { + // have to test on name rather than reference because instances are different + // depending on the type that GetProperties was called on + int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name); + if (index == -1) + { + initialProperties.Add(subTypeProperty); + } + else + { + PropertyInfo childProperty = initialProperties[index]; + // don't replace public child with private base + if (!IsPublic(childProperty)) + { + // replace nonpublic properties for a child, but gotten from + // the parent with the one from the child + // the property gotten from the child will have access to private getter/setter + initialProperties[index] = subTypeProperty; + } + } + } + else + { + if (!subTypeProperty.IsVirtual()) + { + int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name + && p.DeclaringType == subTypeProperty.DeclaringType); + + if (index == -1) + initialProperties.Add(subTypeProperty); + } + else + { + int index = initialProperties.IndexOf(p => p.Name == subTypeProperty.Name + && p.IsVirtual() + && p.GetBaseDefinition() != null + && p.GetBaseDefinition().DeclaringType.IsAssignableFrom(subTypeProperty.DeclaringType)); + + if (index == -1) + initialProperties.Add(subTypeProperty); + } + } + } + } + } + + public static bool IsMethodOverridden(Type currentType, Type methodDeclaringType, string method) + { + bool isMethodOverriden = currentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Any(info => + info.Name == method && + // check that the method overrides the original on DynamicObjectProxy + info.DeclaringType != methodDeclaringType + && info.GetBaseDefinition().DeclaringType == methodDeclaringType + ); + + return isMethodOverriden; + } + + public static object GetDefaultValue(Type type) + { + if (!type.IsValueType()) + return null; + + switch (ConvertUtils.GetTypeCode(type)) + { + case PrimitiveTypeCode.Boolean: + return false; + case PrimitiveTypeCode.Char: + case PrimitiveTypeCode.SByte: + case PrimitiveTypeCode.Byte: + case PrimitiveTypeCode.Int16: + case PrimitiveTypeCode.UInt16: + case PrimitiveTypeCode.Int32: + case PrimitiveTypeCode.UInt32: + return 0; + case PrimitiveTypeCode.Int64: + case PrimitiveTypeCode.UInt64: + return 0L; + case PrimitiveTypeCode.Single: + return 0f; + case PrimitiveTypeCode.Double: + return 0.0; + case PrimitiveTypeCode.Decimal: + return 0m; + case PrimitiveTypeCode.DateTime: + return new DateTime(); +#if !(PORTABLE || PORTABLE40 || NET35 || NET20) + case PrimitiveTypeCode.BigInteger: + return new BigInteger(); +#endif + case PrimitiveTypeCode.Guid: + return new Guid(); +#if !NET20 + case PrimitiveTypeCode.DateTimeOffset: + return new DateTimeOffset(); +#endif + } + + if (IsNullable(type)) + return null; + + // possibly use IL initobj for perf here? + return Activator.CreateInstance(type); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/StringBuffer.cs b/Libs/JsonNet/Utilities/StringBuffer.cs new file mode 100644 index 0000000..0502239 --- /dev/null +++ b/Libs/JsonNet/Utilities/StringBuffer.cs @@ -0,0 +1,107 @@ +#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; + +namespace Newtonsoft.Json.Utilities +{ + /// + /// Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. + /// + internal class StringBuffer + { + private char[] _buffer; + private int _position; + + private static readonly char[] EmptyBuffer = new char[0]; + + public int Position + { + get { return _position; } + set { _position = value; } + } + + public StringBuffer() + { + _buffer = EmptyBuffer; + } + + public StringBuffer(int initalSize) + { + _buffer = new char[initalSize]; + } + + public void Append(char value) + { + // test if the buffer array is large enough to take the value + if (_position == _buffer.Length) + EnsureSize(1); + + // set value and increment poisition + _buffer[_position++] = value; + } + + public void Append(char[] buffer, int startIndex, int count) + { + if (_position + count >= _buffer.Length) + EnsureSize(count); + + Array.Copy(buffer, startIndex, _buffer, _position, count); + + _position += count; + } + + public void Clear() + { + _buffer = EmptyBuffer; + _position = 0; + } + + private void EnsureSize(int appendLength) + { + char[] newBuffer = new char[(_position + appendLength) * 2]; + + Array.Copy(_buffer, newBuffer, _position); + + _buffer = newBuffer; + } + + public override string ToString() + { + return ToString(0, _position); + } + + public string ToString(int start, int length) + { + // TODO: validation + return new string(_buffer, start, length); + } + + public char[] GetInternalBuffer() + { + return _buffer; + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/StringReference.cs b/Libs/JsonNet/Utilities/StringReference.cs new file mode 100644 index 0000000..0feaa49 --- /dev/null +++ b/Libs/JsonNet/Utilities/StringReference.cs @@ -0,0 +1,61 @@ +#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 + +namespace Newtonsoft.Json.Utilities +{ + internal struct StringReference + { + private readonly char[] _chars; + private readonly int _startIndex; + private readonly int _length; + + public char[] Chars + { + get { return _chars; } + } + + public int StartIndex + { + get { return _startIndex; } + } + + public int Length + { + get { return _length; } + } + + public StringReference(char[] chars, int startIndex, int length) + { + _chars = chars; + _startIndex = startIndex; + _length = length; + } + + public override string ToString() + { + return new string(_chars, _startIndex, _length); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/StringUtils.cs b/Libs/JsonNet/Utilities/StringUtils.cs new file mode 100644 index 0000000..975f196 --- /dev/null +++ b/Libs/JsonNet/Utilities/StringUtils.cs @@ -0,0 +1,211 @@ +#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.IO; +using System.Text; +using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class StringUtils + { + public const string CarriageReturnLineFeed = "\r\n"; + public const string Empty = ""; + public const char CarriageReturn = '\r'; + public const char LineFeed = '\n'; + public const char Tab = '\t'; + + public static string FormatWith(this string format, IFormatProvider provider, object arg0) + { + return format.FormatWith(provider, new[] { arg0 }); + } + + public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1) + { + return format.FormatWith(provider, new[] { arg0, arg1 }); + } + + public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2) + { + return format.FormatWith(provider, new[] { arg0, arg1, arg2 }); + } + + public static string FormatWith(this string format, IFormatProvider provider, object arg0, object arg1, object arg2, object arg3) + { + return format.FormatWith(provider, new[] { arg0, arg1, arg2, arg3 }); + } + + private static string FormatWith(this string format, IFormatProvider provider, params object[] args) + { + // leave this a private to force code to use an explicit overload + // avoids stack memory being reserved for the object array + ValidationUtils.ArgumentNotNull(format, "format"); + + return string.Format(provider, format, args); + } + + /// + /// Determines whether the string is all white space. Empty string will return false. + /// + /// The string to test whether it is all white space. + /// + /// true if the string is all white space; otherwise, false. + /// + public static bool IsWhiteSpace(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (s.Length == 0) + return false; + + for (int i = 0; i < s.Length; i++) + { + if (!char.IsWhiteSpace(s[i])) + return false; + } + + return true; + } + + /// + /// Nulls an empty string. + /// + /// The string. + /// Null if the string was null, otherwise the string unchanged. + public static string NullEmptyString(string s) + { + return (string.IsNullOrEmpty(s)) ? null : s; + } + + public static StringWriter CreateStringWriter(int capacity) + { + StringBuilder sb = new StringBuilder(capacity); + StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); + + return sw; + } + + public static int? GetLength(string value) + { + if (value == null) + return null; + else + return value.Length; + } + + public static void ToCharAsUnicode(char c, char[] buffer) + { + buffer[0] = '\\'; + buffer[1] = 'u'; + buffer[2] = MathUtils.IntToHex((c >> 12) & '\x000f'); + buffer[3] = MathUtils.IntToHex((c >> 8) & '\x000f'); + buffer[4] = MathUtils.IntToHex((c >> 4) & '\x000f'); + buffer[5] = MathUtils.IntToHex(c & '\x000f'); + } + + public static TSource ForgivingCaseSensitiveFind(this IEnumerable source, Func valueSelector, string testValue) + { + if (source == null) + throw new ArgumentNullException("source"); + if (valueSelector == null) + throw new ArgumentNullException("valueSelector"); + + var caseInsensitiveResults = source.Where(s => string.Equals(valueSelector(s), testValue, StringComparison.OrdinalIgnoreCase)); + if (caseInsensitiveResults.Count() <= 1) + { + return caseInsensitiveResults.SingleOrDefault(); + } + else + { + // multiple results returned. now filter using case sensitivity + var caseSensitiveResults = source.Where(s => string.Equals(valueSelector(s), testValue, StringComparison.Ordinal)); + return caseSensitiveResults.SingleOrDefault(); + } + } + + public static string ToCamelCase(string s) + { + if (string.IsNullOrEmpty(s)) + return s; + + if (!char.IsUpper(s[0])) + return s; + + char[] chars = s.ToCharArray(); + + for (int i = 0; i < chars.Length; i++) + { + bool hasNext = (i + 1 < chars.Length); + if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) + break; + +#if !(NETFX_CORE || PORTABLE) + chars[i] = char.ToLower(chars[i], CultureInfo.InvariantCulture); +#else + chars[i] = char.ToLowerInvariant(chars[i]); +#endif + } + + return new string(chars); + } + + public static bool IsHighSurrogate(char c) + { +#if !(PORTABLE40 || PORTABLE) + return char.IsHighSurrogate(c); +#else + return (c >= 55296 && c <= 56319); +#endif + } + + public static bool IsLowSurrogate(char c) + { +#if !(PORTABLE40 || PORTABLE) + return char.IsLowSurrogate(c); +#else + return (c >= 56320 && c <= 57343); +#endif + } + + public static bool StartsWith(this string source, char value) + { + return (source.Length > 0 && source[0] == value); + } + + public static bool EndsWith(this string source, char value) + { + return (source.Length > 0 && source[source.Length - 1] == value); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ThreadSafeStore.cs b/Libs/JsonNet/Utilities/ThreadSafeStore.cs new file mode 100644 index 0000000..d961126 --- /dev/null +++ b/Libs/JsonNet/Utilities/ThreadSafeStore.cs @@ -0,0 +1,91 @@ +#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; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#endif +using System.Threading; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal class ThreadSafeStore + { + private readonly object _lock = new object(); + private Dictionary _store; + private readonly Func _creator; + + public ThreadSafeStore(Func creator) + { + if (creator == null) + throw new ArgumentNullException("creator"); + + _creator = creator; + _store = new Dictionary(); + } + + public TValue Get(TKey key) + { + TValue value; + if (!_store.TryGetValue(key, out value)) + return AddValue(key); + + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _creator(key); + + lock (_lock) + { + if (_store == null) + { + _store = new Dictionary(); + _store[key] = value; + } + else + { + // double check locking + TValue checkValue; + if (_store.TryGetValue(key, out checkValue)) + return checkValue; + + Dictionary newStore = new Dictionary(_store); + newStore[key] = value; + +#if !(NETFX_CORE || PORTABLE) + Thread.MemoryBarrier(); +#endif + _store = newStore; + } + + return value; + } + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/TypeExtensions.cs b/Libs/JsonNet/Utilities/TypeExtensions.cs new file mode 100644 index 0000000..3618cae --- /dev/null +++ b/Libs/JsonNet/Utilities/TypeExtensions.cs @@ -0,0 +1,575 @@ +#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.Reflection; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal static class TypeExtensions + { +#if NETFX_CORE || PORTABLE + private static BindingFlags DefaultFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.GetGetMethod(false); + } + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo, bool nonPublic) + { + MethodInfo getMethod = propertyInfo.GetMethod; + if (getMethod != null && (getMethod.IsPublic || nonPublic)) + return getMethod; + + return null; + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) + { + return propertyInfo.GetSetMethod(false); + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo, bool nonPublic) + { + MethodInfo setMethod = propertyInfo.SetMethod; + if (setMethod != null && (setMethod.IsPublic || nonPublic)) + return setMethod; + + return null; + } + + public static bool IsSubclassOf(this Type type, Type c) + { + return type.GetTypeInfo().IsSubclassOf(c); + } + + public static bool IsAssignableFrom(this Type type, Type c) + { + return type.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo()); + } +#endif + + public static MethodInfo Method(this Delegate d) + { +#if !(NETFX_CORE || PORTABLE) + return d.Method; +#else + return d.GetMethodInfo(); +#endif + } + + public static MemberTypes MemberType(this MemberInfo memberInfo) + { +#if !(NETFX_CORE || PORTABLE || PORTABLE40) + return memberInfo.MemberType; +#else + if (memberInfo is PropertyInfo) + return MemberTypes.Property; + else if (memberInfo is FieldInfo) + return MemberTypes.Field; + else if (memberInfo is EventInfo) + return MemberTypes.Event; + else if (memberInfo is MethodInfo) + return MemberTypes.Method; + else + return MemberTypes.Other; +#endif + } + + public static bool ContainsGenericParameters(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.ContainsGenericParameters; +#else + return type.GetTypeInfo().ContainsGenericParameters; +#endif + } + + public static bool IsInterface(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsInterface; +#else + return type.GetTypeInfo().IsInterface; +#endif + } + + public static bool IsGenericType(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsGenericType; +#else + return type.GetTypeInfo().IsGenericType; +#endif + } + + public static bool IsGenericTypeDefinition(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsGenericTypeDefinition; +#else + return type.GetTypeInfo().IsGenericTypeDefinition; +#endif + } + + public static Type BaseType(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.BaseType; +#else + return type.GetTypeInfo().BaseType; +#endif + } + + public static Assembly Assembly(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.Assembly; +#else + return type.GetTypeInfo().Assembly; +#endif + } + + public static bool IsEnum(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsEnum; +#else + return type.GetTypeInfo().IsEnum; +#endif + } + + public static bool IsClass(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsClass; +#else + return type.GetTypeInfo().IsClass; +#endif + } + + public static bool IsSealed(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsSealed; +#else + return type.GetTypeInfo().IsSealed; +#endif + } + +#if PORTABLE40 + public static PropertyInfo GetProperty(this Type type, string name, BindingFlags bindingFlags, object placeholder1, Type propertyType, IList indexParameters, object placeholder2) + { + IList propertyInfos = type.GetProperties(bindingFlags); + + return propertyInfos.Where(p => + { + if (name != null && name != p.Name) + return false; + if (propertyType != null && propertyType != p.PropertyType) + return false; + if (indexParameters != null) + { + if (!p.GetIndexParameters().Select(ip => ip.ParameterType).SequenceEqual(indexParameters)) + return false; + } + + return true; + }).SingleOrDefault(); + } + + public static IEnumerable GetMember(this Type type, string name, MemberTypes memberType, BindingFlags bindingFlags) + { + return type.GetMembers(bindingFlags).Where(m => + { + if (name != null && name != m.Name) + return false; + if (m.MemberType() != memberType) + return false; + + return true; + }); + } +#endif + +#if (NETFX_CORE || PORTABLE) + public static MethodInfo GetBaseDefinition(this MethodInfo method) + { + return method.GetRuntimeBaseDefinition(); + } +#endif + +#if (NETFX_CORE || PORTABLE) + public static bool IsDefined(this Type type, Type attributeType, bool inherit) + { + return type.GetTypeInfo().CustomAttributes.Any(a => a.AttributeType == attributeType); + } + + public static MethodInfo GetMethod(this Type type, string name) + { + return type.GetMethod(name, DefaultFlags); + } + + public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags) + { + return type.GetTypeInfo().GetDeclaredMethod(name); + } + + public static MethodInfo GetMethod(this Type type, IList parameterTypes) + { + return type.GetMethod(null, parameterTypes); + } + + public static MethodInfo GetMethod(this Type type, string name, IList parameterTypes) + { + return type.GetMethod(name, DefaultFlags, null, parameterTypes, null); + } + + public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags, object placeHolder1, IList parameterTypes, object placeHolder2) + { + return type.GetTypeInfo().DeclaredMethods.Where(m => + { + if (name != null && m.Name != name) + return false; + + if (!TestAccessibility(m, bindingFlags)) + return false; + + return m.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes); + }).SingleOrDefault(); + } + + public static PropertyInfo GetProperty(this Type type, string name, BindingFlags bindingFlags, object placeholder1, Type propertyType, IList indexParameters, object placeholder2) + { + return type.GetTypeInfo().DeclaredProperties.Where(p => + { + if (name != null && name != p.Name) + return false; + if (propertyType != null && propertyType != p.PropertyType) + return false; + if (indexParameters != null) + { + if (!p.GetIndexParameters().Select(ip => ip.ParameterType).SequenceEqual(indexParameters)) + return false; + } + + return true; + }).SingleOrDefault(); + } + + public static IEnumerable GetMember(this Type type, string name, MemberTypes memberType, BindingFlags bindingFlags) + { + return type.GetTypeInfo().GetMembersRecursive().Where(m => + { + if (name != null && name != m.Name) + return false; + if (m.MemberType() != memberType) + return false; + if (!TestAccessibility(m, bindingFlags)) + return false; + + return true; + }); + } + + public static IEnumerable GetConstructors(this Type type) + { + return type.GetConstructors(DefaultFlags); + } + + public static IEnumerable GetConstructors(this Type type, BindingFlags bindingFlags) + { + return type.GetConstructors(bindingFlags, null); + } + + private static IEnumerable GetConstructors(this Type type, BindingFlags bindingFlags, IList parameterTypes) + { + return type.GetTypeInfo().DeclaredConstructors.Where(c => + { + if (!TestAccessibility(c, bindingFlags)) + return false; + + if (parameterTypes != null && !c.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes)) + return false; + + return true; + }); + } + + public static ConstructorInfo GetConstructor(this Type type, IList parameterTypes) + { + return type.GetConstructor(DefaultFlags, null, parameterTypes, null); + } + + public static ConstructorInfo GetConstructor(this Type type, BindingFlags bindingFlags, object placeholder1, IList parameterTypes, object placeholder2) + { + return type.GetConstructors(bindingFlags, parameterTypes).SingleOrDefault(); + } + + public static MemberInfo[] GetMember(this Type type, string member) + { + return type.GetMember(member, DefaultFlags); + } + + public static MemberInfo[] GetMember(this Type type, string member, BindingFlags bindingFlags) + { + return type.GetTypeInfo().GetMembersRecursive().Where(m => m.Name == member && TestAccessibility(m, bindingFlags)).ToArray(); + } + + public static MemberInfo GetField(this Type type, string member) + { + return type.GetField(member, DefaultFlags); + } + + public static MemberInfo GetField(this Type type, string member, BindingFlags bindingFlags) + { + return type.GetTypeInfo().GetDeclaredField(member); + } + + public static IEnumerable GetProperties(this Type type, BindingFlags bindingFlags) + { + IList properties = (bindingFlags.HasFlag(BindingFlags.DeclaredOnly)) + ? type.GetTypeInfo().DeclaredProperties.ToList() + : type.GetTypeInfo().GetPropertiesRecursive(); + + return properties.Where(p => TestAccessibility(p, bindingFlags)); + } + + private static IList GetMembersRecursive(this TypeInfo type) + { + TypeInfo t = type; + IList members = new List(); + while (t != null) + { + foreach (var member in t.DeclaredMembers) + { + if (!members.Any(p => p.Name == member.Name)) + members.Add(member); + } + t = (t.BaseType != null) ? t.BaseType.GetTypeInfo() : null; + } + + return members; + } + + private static IList GetPropertiesRecursive(this TypeInfo type) + { + TypeInfo t = type; + IList properties = new List(); + while (t != null) + { + foreach (var member in t.DeclaredProperties) + { + if (!properties.Any(p => p.Name == member.Name)) + properties.Add(member); + } + t = (t.BaseType != null) ? t.BaseType.GetTypeInfo() : null; + } + + return properties; + } + + private static IList GetFieldsRecursive(this TypeInfo type) + { + TypeInfo t = type; + IList fields = new List(); + while (t != null) + { + foreach (var member in t.DeclaredFields) + { + if (!fields.Any(p => p.Name == member.Name)) + fields.Add(member); + } + t = (t.BaseType != null) ? t.BaseType.GetTypeInfo() : null; + } + + return fields; + } + + public static IEnumerable GetMethods(this Type type, BindingFlags bindingFlags) + { + return type.GetTypeInfo().DeclaredMethods; + } + + public static PropertyInfo GetProperty(this Type type, string name) + { + return type.GetProperty(name, DefaultFlags); + } + + public static PropertyInfo GetProperty(this Type type, string name, BindingFlags bindingFlags) + { + return type.GetTypeInfo().GetDeclaredProperty(name); + } + + public static IEnumerable GetFields(this Type type) + { + return type.GetFields(DefaultFlags); + } + + public static IEnumerable GetFields(this Type type, BindingFlags bindingFlags) + { + IList fields = (bindingFlags.HasFlag(BindingFlags.DeclaredOnly)) + ? type.GetTypeInfo().DeclaredFields.ToList() + : type.GetTypeInfo().GetFieldsRecursive(); + + return fields.Where(f => TestAccessibility(f, bindingFlags)).ToList(); + } + + private static bool TestAccessibility(PropertyInfo member, BindingFlags bindingFlags) + { + if (member.GetMethod != null && TestAccessibility(member.GetMethod, bindingFlags)) + return true; + + if (member.SetMethod != null && TestAccessibility(member.SetMethod, bindingFlags)) + return true; + + return false; + } + + private static bool TestAccessibility(MemberInfo member, BindingFlags bindingFlags) + { + if (member is FieldInfo) + { + return TestAccessibility((FieldInfo) member, bindingFlags); + } + else if (member is MethodBase) + { + return TestAccessibility((MethodBase) member, bindingFlags); + } + else if (member is PropertyInfo) + { + return TestAccessibility((PropertyInfo) member, bindingFlags); + } + + throw new Exception("Unexpected member type."); + } + + private static bool TestAccessibility(FieldInfo member, BindingFlags bindingFlags) + { + bool visibility = (member.IsPublic && bindingFlags.HasFlag(BindingFlags.Public)) || + (!member.IsPublic && bindingFlags.HasFlag(BindingFlags.NonPublic)); + + bool instance = (member.IsStatic && bindingFlags.HasFlag(BindingFlags.Static)) || + (!member.IsStatic && bindingFlags.HasFlag(BindingFlags.Instance)); + + return visibility && instance; + } + + private static bool TestAccessibility(MethodBase member, BindingFlags bindingFlags) + { + bool visibility = (member.IsPublic && bindingFlags.HasFlag(BindingFlags.Public)) || + (!member.IsPublic && bindingFlags.HasFlag(BindingFlags.NonPublic)); + + bool instance = (member.IsStatic && bindingFlags.HasFlag(BindingFlags.Static)) || + (!member.IsStatic && bindingFlags.HasFlag(BindingFlags.Instance)); + + return visibility && instance; + } + + public static Type[] GetGenericArguments(this Type type) + { + return type.GetTypeInfo().GenericTypeArguments; + } + + public static IEnumerable GetInterfaces(this Type type) + { + return type.GetTypeInfo().ImplementedInterfaces; + } + + public static IEnumerable GetMethods(this Type type) + { + return type.GetTypeInfo().DeclaredMethods; + } +#endif + + public static bool IsAbstract(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsAbstract; +#else + return type.GetTypeInfo().IsAbstract; +#endif + } + + public static bool IsVisible(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsVisible; +#else + return type.GetTypeInfo().IsVisible; +#endif + } + + public static bool IsValueType(this Type type) + { +#if !(NETFX_CORE || PORTABLE) + return type.IsValueType; +#else + return type.GetTypeInfo().IsValueType; +#endif + } + + public static bool AssignableToTypeName(this Type type, string fullTypeName, out Type match) + { + Type current = type; + + while (current != null) + { + if (string.Equals(current.FullName, fullTypeName, StringComparison.Ordinal)) + { + match = current; + return true; + } + + current = current.BaseType(); + } + + foreach (Type i in type.GetInterfaces()) + { + if (string.Equals(i.Name, fullTypeName, StringComparison.Ordinal)) + { + match = type; + return true; + } + } + + match = null; + return false; + } + + public static bool AssignableToTypeName(this Type type, string fullTypeName) + { + Type match; + return type.AssignableToTypeName(fullTypeName, out match); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/Utilities/ValidationUtils.cs b/Libs/JsonNet/Utilities/ValidationUtils.cs new file mode 100644 index 0000000..c27874e --- /dev/null +++ b/Libs/JsonNet/Utilities/ValidationUtils.cs @@ -0,0 +1,59 @@ +#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; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ValidationUtils + { + public static void ArgumentNotNullOrEmpty(string value, string parameterName) + { + if (value == null) + throw new ArgumentNullException(parameterName); + + if (value.Length == 0) + throw new ArgumentException("'{0}' cannot be empty.".FormatWith(CultureInfo.InvariantCulture, parameterName), parameterName); + } + + public static void ArgumentTypeIsEnum(Type enumType, string parameterName) + { + ArgumentNotNull(enumType, "enumType"); + + if (!enumType.IsEnum()) + throw new ArgumentException("Type {0} is not an Enum.".FormatWith(CultureInfo.InvariantCulture, enumType), parameterName); + } + + public static void ArgumentNotNull(object value, string parameterName) + { + if (value == null) + throw new ArgumentNullException(parameterName); + } + } +} \ No newline at end of file diff --git a/Libs/JsonNet/WriteState.cs b/Libs/JsonNet/WriteState.cs new file mode 100644 index 0000000..c5352a5 --- /dev/null +++ b/Libs/JsonNet/WriteState.cs @@ -0,0 +1,72 @@ +#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; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the state of the . + /// + public enum WriteState + { + /// + /// An exception has been thrown, which has left the in an invalid state. + /// You may call the method to put the in the Closed state. + /// Any other method calls results in an being thrown. + /// + Error, + + /// + /// The method has been called. + /// + Closed, + + /// + /// An object is being written. + /// + Object, + + /// + /// A array is being written. + /// + Array, + + /// + /// A constructor is being written. + /// + Constructor, + + /// + /// A property is being written. + /// + Property, + + /// + /// A write method has not been called. + /// + Start + } +} \ No newline at end of file diff --git a/Sources/VKSharp/Api/Generated/ImplementedMethods.txt b/Sources/VKSharp/Api/Generated/ImplementedMethods.txt index 84e9d71..7449c24 100644 --- a/Sources/VKSharp/Api/Generated/ImplementedMethods.txt +++ b/Sources/VKSharp/Api/Generated/ImplementedMethods.txt @@ -1,8 +1,10 @@ -Implemented 143 methods + +Implemented 145 methods * Account * account.banUser * account.getAppPermissions * account.getBanned + * account.getInfo * account.getProfileInfo * account.setInfo * account.setNameInMenu @@ -18,6 +20,7 @@ * audio.deleteAlbum * audio.edit * audio.get + * audio.getAlbums * audio.getById * audio.getById * audio.getCount @@ -161,4 +164,4 @@ * wall.repost * wall.restore * wall.restoreComment - * wall.unpin \ No newline at end of file + * wall.unpin diff --git a/Sources/VKSharp/Api/Generated/RawApi.cs b/Sources/VKSharp/Api/Generated/RawApi.cs index 4598d20..1254e1d 100644 --- a/Sources/VKSharp/Api/Generated/RawApi.cs +++ b/Sources/VKSharp/Api/Generated/RawApi.cs @@ -98,6 +98,10 @@ public async Task SetInfoAsync( uint? intro = null ){ return await _parent.Executor.ExecRawAsync( _parent._reqapi.AccountSetInfo( intro ) ); } + public async Task GetInfoAsync( ){ + return await _parent.Executor.ExecRawAsync( _parent._reqapi.AccountGetInfo( ) ); + } + public async Task GetProfileInfoAsync( ){ return await _parent.Executor.ExecRawAsync( _parent._reqapi.AccountGetProfileInfo( ) ); } @@ -128,6 +132,10 @@ public async Task EditAsync( int ownerId , long audioId , string artist return await _parent.Executor.ExecRawAsync( _parent._reqapi.AudioEdit( ownerId,audioId,artist,title,text,genreId,noSearch ) ); } + public async Task GetAlbumsAsync( int? ownerId = null, uint offset = 0, uint count = 100 ){ + return await _parent.Executor.ExecRawAsync( _parent._reqapi.AudioGetAlbums( ownerId,offset,count ) ); + } + public async Task GetAsync( int? ownerId = null, long? albumId = null, ulong[] audioIds = null, bool needUser = false, uint offset = 0, uint count = 100 ){ return await _parent.Executor.ExecRawAsync( _parent._reqapi.AudioGet( ownerId,albumId,audioIds,needUser,offset,count ) ); } diff --git a/Sources/VKSharp/Api/Generated/RequestApi1.cs b/Sources/VKSharp/Api/Generated/RequestApi1.cs index a930eff..ac6eca5 100644 --- a/Sources/VKSharp/Api/Generated/RequestApi1.cs +++ b/Sources/VKSharp/Api/Generated/RequestApi1.cs @@ -149,6 +149,18 @@ public VKRequest AccountSetInfo( return req; } + public VKRequest AccountGetInfo( + +){ + var req = new VKRequest{ + MethodName = "account.getInfo", + Parameters = new Dictionary { + } + }; + req.Token = CurrentToken; + + return req; + } public VKRequest AccountGetProfileInfo( ){ @@ -237,6 +249,22 @@ public VKRequest AudioEdit( return req; } + public VKRequest> AudioGetAlbums( + int? ownerId = null, uint offset = 0, uint count = 100 +){ + var req = new VKRequest>{ + MethodName = "audio.getAlbums", + Parameters = new Dictionary { + { "owner_id", MiscTools.NullableString(ownerId) }, + { "offset", offset.ToNCString() }, + { "count", count.ToNCString() } + } + }; + if (IsLogged){ + req.Token = CurrentToken; + } + return req; + } public VKRequest> AudioGet( int? ownerId = null, long? albumId = null, ulong[] audioIds = null, bool needUser = false, uint offset = 0, uint count = 100 ){ diff --git a/Sources/VKSharp/Api/Generated/VKSharpFuncs.xml b/Sources/VKSharp/Api/Generated/VKSharpFuncs.xml index 470a75d..4dca544 100644 --- a/Sources/VKSharp/Api/Generated/VKSharpFuncs.xml +++ b/Sources/VKSharp/Api/Generated/VKSharpFuncs.xml @@ -73,6 +73,11 @@ + + + + + @@ -123,6 +128,15 @@ + + + + + + + + + diff --git a/Sources/VKSharp/Api/Generated/VkApi.cs b/Sources/VKSharp/Api/Generated/VkApi.cs index bfa260e..bfcbd97 100644 --- a/Sources/VKSharp/Api/Generated/VkApi.cs +++ b/Sources/VKSharp/Api/Generated/VkApi.cs @@ -119,6 +119,16 @@ await _parent.Executor.ExecAsync( ) ) ; + } + public async Task GetInfoAsync( ){ + return ( + await _parent.Executor.ExecAsync( + _parent._reqapi.AccountGetInfo( + + ) + ) + ).Response + ; } public async Task GetProfileInfoAsync( ){ return ( @@ -180,6 +190,16 @@ await _parent.Executor.ExecAsync( ) ).Response ; + } + public async Task >GetAlbumsAsync( int? ownerId = null, uint offset = 0, uint count = 100 ){ + return ( + await _parent.Executor.ExecAsync( + _parent._reqapi.AudioGetAlbums( + ownerId,offset,count + ) + ) + ).Response + ; } public async Task >GetAsync( int? ownerId = null, long? albumId = null, ulong[] audioIds = null, bool needUser = false, uint offset = 0, uint count = 100 ){ return ( diff --git a/Sources/VKSharp/Api/HandCoded/VKApi.cs b/Sources/VKSharp/Api/HandCoded/VKApi.cs deleted file mode 100644 index 7ee4653..0000000 --- a/Sources/VKSharp/Api/HandCoded/VKApi.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using VKSharp.Core.Entities; -using VKSharp.Data.Parameters; -using VKSharp.Data.Request; -using VKSharp.Helpers; - -namespace VKSharp { - public partial class VKApi { - //public async Task UsersSearchAsync( UsersSearchParams usersSearchParams ) { - // var req = new VKRequest { - // MethodName = "users.search", - // Parameters = - // new Dictionary { - // { "q", usersSearchParams.Query }, - // { "sort", ( (int) usersSearchParams.Sort ).ToNCString() }, - // { "offset", MiscTools.NullableString( usersSearchParams.Offset ) }, - // { "count", MiscTools.NullableString( usersSearchParams.Count ) }, - // { "fields", String.Join( ",", MiscTools.GetUserFields( usersSearchParams.Fields ) ) }, - // { "city", MiscTools.NullableString( usersSearchParams.CityId ) }, - // { "country", MiscTools.NullableString( usersSearchParams.CountryId ) }, - // { "hometown", usersSearchParams.Hometown }, - // { "university_country", MiscTools.NullableString( usersSearchParams.UniversityCountryId ) }, - // { "university", MiscTools.NullableString( usersSearchParams.UniversityId ) }, - // { "university_year", MiscTools.NullableString( usersSearchParams.UniversityYear ) }, - // { "sex", usersSearchParams.Sex != null ? ( (int) usersSearchParams.Sex ).ToNCString() : "" }, - // { "status", MiscTools.NullableString( usersSearchParams.Relation ) }, - // { "age_from", MiscTools.NullableString( usersSearchParams.AgeFrom ) }, - // { "age_to", MiscTools.NullableString( usersSearchParams.AgeTo ) }, - // { "birth_day", MiscTools.NullableString( usersSearchParams.BirthDay ) }, - // { "birth_month", MiscTools.NullableString( usersSearchParams.BirthMonth ) }, - // { "birth_year", MiscTools.NullableString( usersSearchParams.BirthYear ) }, - // { "online", MiscTools.NullableString( usersSearchParams.Online.HasValue ? (uint?) ( usersSearchParams.Online.Value ? 1 : 0 ) : null ) }, - // { "has_photo", MiscTools.NullableString( usersSearchParams.HasPhoto.HasValue ? (uint?) ( usersSearchParams.HasPhoto.Value ? 1 : 0 ) : null ) }, - // { "school_country", MiscTools.NullableString( usersSearchParams.SchoolCountryId ) }, - // { "school_city", MiscTools.NullableString( usersSearchParams.SchoolCityId ) }, - // { "school", MiscTools.NullableString( usersSearchParams.SchoolId ) }, - // { "school_year", MiscTools.NullableString( usersSearchParams.SchoolYear ) }, - // { "religion", usersSearchParams.Religion }, - // { "interests", usersSearchParams.Interests }, - // { "company", usersSearchParams.Company }, - // { "position", usersSearchParams.Position }, - // { "group_id", MiscTools.NullableString( usersSearchParams.GroupId ) } - // }, - // Token = IsLogged ? CurrentToken : null - // }; - // return ( await _executor.ExecAsync( req ) ).Data; - //} - - //TODO: extended view is not supported - } -} \ No newline at end of file diff --git a/Sources/VKSharp/Core/Entities/AudioAlbum.cs b/Sources/VKSharp/Core/Entities/AudioAlbum.cs new file mode 100644 index 0000000..01b0f54 --- /dev/null +++ b/Sources/VKSharp/Core/Entities/AudioAlbum.cs @@ -0,0 +1,7 @@ +using VKSharp.Core.Interfaces; + +namespace VKSharp.Core.Entities { + public class AudioAlbum : OwnedEntity { + public string Title { get; set; } + } +} diff --git a/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParser.cs b/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParser.cs deleted file mode 100644 index f77d9e9..0000000 --- a/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -using VKSharp.Data.Executors; - -namespace VKSharp.Core.Interfaces { - public interface IXmlVKEntityParser { - IExecutor Executor { get; set; } - } -} diff --git a/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParserT.cs b/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParserT.cs deleted file mode 100644 index 5757ad8..0000000 --- a/Sources/VKSharp/Core/Interfaces/IXmlVKEntityParserT.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Xml.Linq; - -namespace VKSharp.Core.Interfaces { - public interface IXmlVKEntityParser : IXmlVKEntityParser where T : IVKEntity{ - // - void FillFromXml( IEnumerable nodes, T entity ); - //parse one from xml node - T ParseFromXml( XElement node ); - //same for array - T[] ParseAllFromXml( IEnumerable nodes ); - //parse from set of nodёs - T ParseFromXmlFragments( IEnumerable nodes ); - //update - bool UpdateFromFragment( XElement node, T entity ); - } -} \ No newline at end of file diff --git a/Sources/VKSharp/Core/ResponseEntities/AccountInfo.cs b/Sources/VKSharp/Core/ResponseEntities/AccountInfo.cs new file mode 100644 index 0000000..61652a9 --- /dev/null +++ b/Sources/VKSharp/Core/ResponseEntities/AccountInfo.cs @@ -0,0 +1,8 @@ +namespace VKSharp.Core.ResponseEntities { + public class AccountInfo { + public string Country { get; set; } + public bool? HttpsRequired { get; set; } + public int? Intro { get; set; } + public int? Language { get; set; } + } +} diff --git a/Sources/VKSharp/Data/Executors/JsonExecutor.cs b/Sources/VKSharp/Data/Executors/JsonExecutor.cs index 7253eb8..b8adf45 100644 --- a/Sources/VKSharp/Data/Executors/JsonExecutor.cs +++ b/Sources/VKSharp/Data/Executors/JsonExecutor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -7,13 +8,29 @@ using System.Reflection; using System.Threading.Tasks; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; using VKSharp.Data.Request; using VKSharp.Helpers; using VKSharp.Helpers.Exceptions; namespace VKSharp.Data.Executors { public class JsonExecutor : IExecutor { + static JsonExecutor() { + ServicePointManager.DefaultConnectionLimit = Math.Max( 25, ServicePointManager.DefaultConnectionLimit ); + var httpClientHandler = new HttpClientHandler(); + if ( httpClientHandler.SupportsAutomaticDecompression ) + httpClientHandler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; + Client = new HttpClient( httpClientHandler ); + var snakeCaseContractResolver = new SnakeCaseContractResolver(); + snakeCaseContractResolver.DefaultMembersSearchFlags |= BindingFlags.NonPublic; + Jsonser = new JsonSerializer { ContractResolver = snakeCaseContractResolver }; + Jsonser.Converters.Add( new SnakeCaseEnumConverter() { + AllowIntegerValues = true, + CamelCaseText = false + } ); + } #region IO private const string ReqExt = "json"; private static readonly HttpClient Client; @@ -34,21 +51,44 @@ private static async Task ExecRawAsync( VKRequest request, string #endregion #region Serialization private static readonly JsonSerializer Jsonser; - - static JsonExecutor() { - ServicePointManager.DefaultConnectionLimit = Math.Max(25, ServicePointManager.DefaultConnectionLimit); - var httpClientHandler = new HttpClientHandler(); - if (httpClientHandler.SupportsAutomaticDecompression) - httpClientHandler.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; - Client = new HttpClient( httpClientHandler ); - var snakeCaseContractResolver = new SnakeCaseContractResolver(); - snakeCaseContractResolver.DefaultMembersSearchFlags |= BindingFlags.NonPublic; - Jsonser = new JsonSerializer { ContractResolver = snakeCaseContractResolver }; - } - - internal class SnakeCaseContractResolver : DefaultContractResolver { + private class SnakeCaseContractResolver : DefaultContractResolver { protected override string ResolvePropertyName( string propertyName ) => propertyName.ToSnake(); } + //todo:implement + private class SnakeCaseEnumConverter : StringEnumConverter { + public override bool CanConvert( Type objectType ) { + var btype = objectType; + if ( objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) + btype = Nullable.GetUnderlyingType( objectType ); + return btype.IsEnum; + } + public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) { + //base + var isNullable = ReflectionUtils.IsNullableType( objectType ); + var t = isNullable ? Nullable.GetUnderlyingType( objectType ) : objectType; + if ( reader.TokenType == JsonToken.Null ) { + if ( !ReflectionUtils.IsNullableType( objectType ) ) + throw JsonSerializationException.Create( reader, "Cannot convert null value to {0}.".FormatWith( CultureInfo.InvariantCulture, objectType ) ); + return null; + } + try { + switch ( reader.TokenType ) { + case JsonToken.String: + var enumText = reader.Value.ToString().ToMeth(); + return EnumUtils.ParseEnumName( enumText, isNullable, t ); + case JsonToken.Integer: + if ( !AllowIntegerValues ) + throw JsonSerializationException.Create( reader, "Integer value {0} is not allowed.".FormatWith( CultureInfo.InvariantCulture, reader.Value ) ); + return ConvertUtils.ConvertOrCast( reader.Value, CultureInfo.InvariantCulture, t ); + } + } + catch ( Exception ex ) { + throw JsonSerializationException.Create( reader, "Error converting value {0} to type '{1}'.".FormatWith( CultureInfo.InvariantCulture, MiscellaneousUtils.FormatValueForPrint( reader.Value ), objectType ), ex ); + } + // we don't actually expect to get here. + throw JsonSerializationException.Create( reader, "Unexpected token {0} when parsing enum.".FormatWith( CultureInfo.InvariantCulture, reader.TokenType ) ); + } + } public VKResponse Parse( string input ) { using ( var sr = new StringReader( input ) ) diff --git a/Sources/VKSharp/Helpers/MiscTools.cs b/Sources/VKSharp/Helpers/MiscTools.cs index 181eb45..9e116d4 100644 --- a/Sources/VKSharp/Helpers/MiscTools.cs +++ b/Sources/VKSharp/Helpers/MiscTools.cs @@ -27,6 +27,18 @@ public static string ToSnake( this string name ) { return t.ToString(); } + public static string ToMeth( this string name, bool p = false ) { + var t = new StringBuilder(); + t.Append( p ? Char.ToLower( name[ 0 ] ) : Char.ToUpper( name[ 0 ] ) ); + for ( var index = 1; index < name.Length; index++ ) { + var c = name[ index ]; + //add '_' before numbers and capitals + if ( c == '.' || c == '_' ) t.Append( Char.ToUpper( name[ ++index ] ) ); + else t.Append( c ); + } + return t.ToString(); + } + public static string[] GetGroupFields( GroupFields fields ) { var s = Enum.GetValues( typeof( GroupFields ) ) diff --git a/Sources/VKSharp/Properties/AssemblyInfo.cs b/Sources/VKSharp/Properties/AssemblyInfo.cs index 5f95ba0..8289519 100644 --- a/Sources/VKSharp/Properties/AssemblyInfo.cs +++ b/Sources/VKSharp/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // Управление общими сведениями о сборке осуществляется с помощью @@ -32,4 +33,4 @@ // используя "*", как показано ниже: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion( "1.0.1.*" )] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/Sources/VKSharp/VKSharp.csproj b/Sources/VKSharp/VKSharp.csproj index 97cde2a..bbf39ee 100644 --- a/Sources/VKSharp/VKSharp.csproj +++ b/Sources/VKSharp/VKSharp.csproj @@ -31,10 +31,6 @@ 4 - - False - packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll - @@ -72,8 +68,10 @@ + + @@ -103,9 +101,7 @@ - - @@ -162,7 +158,6 @@ - @@ -219,6 +214,12 @@ + + + {eeea67f9-9089-481d-a4dc-0966dec9fd7d} + Newtonsoft.Json + + diff --git a/Sources/VKSharp/VKSharp.sln b/Sources/VKSharp/VKSharp.sln index d6544bc..32d5680 100644 --- a/Sources/VKSharp/VKSharp.sln +++ b/Sources/VKSharp/VKSharp.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30501.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.22310.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VKSharp", "VKSharp.csproj", "{E30B8CEF-DC6F-4A66-B92E-CB608D5DADE8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "..\TestApp\TestApp.csproj", "{D70BC0FC-BFEC-4942-95A1-889D60F1536F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json", "..\..\Libs\JsonNet\Newtonsoft.Json.csproj", "{EEEA67F9-9089-481D-A4DC-0966DEC9FD7D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {D70BC0FC-BFEC-4942-95A1-889D60F1536F}.Debug|Any CPU.Build.0 = Debug|Any CPU {D70BC0FC-BFEC-4942-95A1-889D60F1536F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D70BC0FC-BFEC-4942-95A1-889D60F1536F}.Release|Any CPU.Build.0 = Release|Any CPU + {EEEA67F9-9089-481D-A4DC-0966DEC9FD7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEEA67F9-9089-481D-A4DC-0966DEC9FD7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEEA67F9-9089-481D-A4DC-0966DEC9FD7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEEA67F9-9089-481D-A4DC-0966DEC9FD7D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sources/VKSharp/packages.config b/Sources/VKSharp/packages.config index 3916f6b..a40e0fd 100644 --- a/Sources/VKSharp/packages.config +++ b/Sources/VKSharp/packages.config @@ -3,5 +3,5 @@ - + \ No newline at end of file