diff options
author | JamesNK <james@newtonking.com> | 2008-02-28 12:32:09 +0300 |
---|---|---|
committer | JamesNK <james@newtonking.com> | 2008-02-28 12:32:09 +0300 |
commit | 3109027c75beca09701ade023cc0e5f4a439e380 (patch) | |
tree | 7e6da83b7660e5b9be61d78465cee3163c4f787e /Src/Newtonsoft.Json | |
parent | c1ce4c366fb965c6028d0e766c542504e9a54ee1 (diff) |
New features:
Added support for deserializing to an anonymous type with the DeserializeAnonymousType method.
Support for reading, writing and serializing the new DateTimeOffset type.
Added IsoDateTimeConverter. Converts DateTimes and DateTimeOffsets to and from the ISO 8601 format.
Added JavaScriptDateTimeConverter. Converts DateTimes and DateTimeOffsets to and from a JavaScript date constructor.
XmlNodeConverter handles serializing and deserializing JavaScript constructors.
Ability to force XmlNodeConverter to write a value in an array. Logic is controlled by an attribute, json:Array="true".
Changes:
Dates are now serialized in a JSON complient manner, similar to ASP.NET's JavaScriptSerializer or WCF's DataContractJsonSerializer - http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx
Removed the AspNetAjaxDateTimeConverter.
Diffstat (limited to 'Src/Newtonsoft.Json')
-rw-r--r-- | Src/Newtonsoft.Json/Converters/AspNetAjaxDateTimeConverter.cs | 59 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs | 74 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs | 64 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs | 15 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs | 72 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JavaScriptConvert.cs | 67 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JsonConverter.cs | 4 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JsonReader.cs | 78 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JsonSerializer.cs | 126 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/JsonWriter.cs | 9 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 5 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs | 33 | ||||
-rw-r--r-- | Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs | 2 |
13 files changed, 481 insertions, 127 deletions
diff --git a/Src/Newtonsoft.Json/Converters/AspNetAjaxDateTimeConverter.cs b/Src/Newtonsoft.Json/Converters/AspNetAjaxDateTimeConverter.cs deleted file mode 100644 index eaaf83f..0000000 --- a/Src/Newtonsoft.Json/Converters/AspNetAjaxDateTimeConverter.cs +++ /dev/null @@ -1,59 +0,0 @@ -#region License
-// Copyright (c) 2007 James Newton-King
-//
-// Permission is hereby granted, free of charge, to any person
-// obtaining a copy of this software and associated documentation
-// files (the "Software"), to deal in the Software without
-// restriction, including without limitation the rights to use,
-// copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following
-// conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-#endregion
-
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Globalization;
-
-namespace Newtonsoft.Json.Converters
-{
- public class AspNetAjaxDateTimeConverter : JsonConverter
- {
- public override void WriteJson(JsonWriter writer, object value)
- {
- DateTime dateTime = (DateTime)value;
- long javaScriptTicks = JavaScriptConvert.ConvertDateTimeToJavaScriptTicks(dateTime);
-
- writer.WriteValue("@" + javaScriptTicks.ToString(null, CultureInfo.InvariantCulture) + "@");
- }
-
- public override object ReadJson(JsonReader reader, Type objectType)
- {
- string dateTimeText = (string)reader.Value;
- dateTimeText = dateTimeText.Substring(1, dateTimeText.Length - 2);
-
- long javaScriptTicks = Convert.ToInt64(dateTimeText);
-
- return JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
- }
-
- public override bool CanConvert(Type valueType)
- {
- return typeof(DateTime).IsAssignableFrom(valueType);
- }
- }
-}
diff --git a/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs b/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs new file mode 100644 index 0000000..8d06729 --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs @@ -0,0 +1,74 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Globalization;
+
+namespace Newtonsoft.Json.Converters
+{
+ public class IsoDateTimeConverter : JsonConverter
+ {
+ private const string DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
+
+ private DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind;
+ //private bool _forceUniversalTime;
+
+ //public bool ForceUniversalTime
+ //{
+ // get { return _forceUniversalTime; }
+ // set { _forceUniversalTime = value; }
+ //}
+
+ public DateTimeStyles DateTimeStyles
+ {
+ get { return _dateTimeStyles; }
+ set { _dateTimeStyles = value; }
+ }
+
+ public override void WriteJson(JsonWriter writer, object value)
+ {
+ 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, CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
+ if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
+ || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
+ dateTimeOffset = dateTimeOffset.ToUniversalTime();
+
+ text = dateTimeOffset.ToString(DateTimeFormat, CultureInfo.InvariantCulture);
+ }
+
+ writer.WriteValue(text);
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType)
+ {
+ if (reader.TokenType != JsonToken.String)
+ throw new Exception(string.Format("Unexpected token parsing date. Expected String, got {0}.", reader.TokenType));
+
+ string dateText = reader.Value.ToString();
+
+ if (objectType == typeof(DateTimeOffset))
+ return DateTimeOffset.Parse(dateText, CultureInfo.InvariantCulture, _dateTimeStyles);
+
+ return DateTime.Parse(dateText, CultureInfo.InvariantCulture, _dateTimeStyles);
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return (typeof(DateTime).IsAssignableFrom(objectType)
+ || typeof(DateTimeOffset).IsAssignableFrom(objectType));
+ }
+ }
+}
diff --git a/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs b/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs new file mode 100644 index 0000000..e74698c --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs @@ -0,0 +1,64 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Globalization;
+
+namespace Newtonsoft.Json.Converters
+{
+ public class JavaScriptDateTimeConverter : JsonConverter
+ {
+ public override void WriteJson(JsonWriter writer, object value)
+ {
+ long ticks;
+
+ if (value is DateTime)
+ {
+ DateTime dateTime = (DateTime)value;
+ DateTime utcDateTime = dateTime.ToUniversalTime();
+ ticks = JavaScriptConvert.ConvertDateTimeToJavaScriptTicks(utcDateTime);
+ }
+ else
+ {
+ DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
+ DateTimeOffset utcDateTimeOffset = dateTimeOffset.ToUniversalTime();
+ ticks = JavaScriptConvert.ConvertDateTimeToJavaScriptTicks(utcDateTimeOffset.UtcDateTime);
+ }
+
+ writer.WriteStartConstructor("Date");
+ writer.WriteValue(ticks);
+ writer.WriteEndConstructor();
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType)
+ {
+ if (reader.TokenType != JsonToken.StartConstructor || string.Compare(reader.Value.ToString(), "Date", StringComparison.Ordinal) != 0)
+ throw new Exception(string.Format("Unexpected token or value when parsing date. Token: {0}, Value: {1}", reader.TokenType, reader.Value));
+
+ reader.Read();
+
+ if (reader.TokenType != JsonToken.Integer)
+ throw new Exception(string.Format("Unexpected token parsing date. Expected Integer, got {0}.", reader.TokenType));
+
+ long ticks = (long)reader.Value;
+
+ DateTime d = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(ticks);
+
+ reader.Read();
+
+ if (reader.TokenType != JsonToken.EndConstructor)
+ throw new Exception(string.Format("Unexpected token parsing date. Expected EndConstructor, got {0}.", reader.TokenType));
+
+ if (objectType == typeof(DateTimeOffset))
+ return new DateTimeOffset(d);
+
+ return d;
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return (typeof(DateTime).IsAssignableFrom(objectType)
+ || typeof(DateTimeOffset).IsAssignableFrom(objectType));
+ }
+ }
+}
diff --git a/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs b/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs new file mode 100644 index 0000000..4b9f310 --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs @@ -0,0 +1,15 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Newtonsoft.Json.Converters
+{
+ public enum JsonDateTimeSerializationMode
+ {
+ Local,
+ Utc,
+ Unspecified,
+ RoundtripKind
+ }
+}
\ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index 3c5a7ad..0ff6a1f 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -29,6 +29,7 @@ using System.Text; using System.Drawing;
using System.Xml;
using Newtonsoft.Json.Utilities;
+using System.Linq;
namespace Newtonsoft.Json.Converters
{
@@ -40,6 +41,7 @@ namespace Newtonsoft.Json.Converters 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";
#region Writing
public override void WriteJson(JsonWriter writer, object value)
@@ -106,13 +108,29 @@ namespace Newtonsoft.Json.Converters foreach (KeyValuePair<string, List<XmlNode>> nodeNameGroup in nodesGroupedByName)
{
List<XmlNode> groupedNodes = nodeNameGroup.Value;
+ bool writeArray;
if (groupedNodes.Count == 1)
{
+ XmlNode singleNode = groupedNodes[0];
+ XmlAttribute jsonArrayAttribute = (singleNode.Attributes != null) ? singleNode.Attributes["Array", JsonNamespaceUri] : null;
+ if (jsonArrayAttribute != null)
+ writeArray = XmlConvert.ToBoolean(jsonArrayAttribute.Value);
+ else
+ writeArray = false;
+ }
+ else
+ {
+ writeArray = true;
+ }
+
+ if (!writeArray)
+ {
SerializeNode(writer, groupedNodes[0], true);
}
else
{
+ string elementNames = nodeNameGroup.Key;
writer.WritePropertyName(nodeNameGroup.Key);
writer.WriteStartArray();
@@ -138,7 +156,7 @@ namespace Newtonsoft.Json.Converters if (writePropertyName)
writer.WritePropertyName(node.Name);
- if (CollectionUtils.IsNullOrEmpty(node.Attributes) && node.ChildNodes.Count == 1
+ if (ValueAttributes(node.Attributes).Count() == 0 && node.ChildNodes.Count == 1
&& node.ChildNodes[0].NodeType == XmlNodeType.Text)
{
// write elements with a single text child as a name value pair
@@ -149,6 +167,20 @@ namespace Newtonsoft.Json.Converters // empty element
writer.WriteNull();
}
+ else if (node.ChildNodes.OfType<XmlElement>().Where(x => x.Name.StartsWith("-")).Count() > 1)
+ {
+ XmlElement constructorValueElement = node.ChildNodes.OfType<XmlElement>().Where(x => x.Name.StartsWith("-")).First();
+ string constructorName = constructorValueElement.Name.Substring(1);
+
+ writer.WriteStartConstructor(constructorName);
+
+ for (int i = 0; i < node.ChildNodes.Count; i++)
+ {
+ SerializeNode(writer, node.ChildNodes[i], false);
+ }
+
+ writer.WriteEndConstructor();
+ }
else
{
writer.WriteStartObject();
@@ -174,6 +206,11 @@ namespace Newtonsoft.Json.Converters case XmlNodeType.ProcessingInstruction:
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
+ if (node.Prefix == "xmlns" && node.Value == JsonNamespaceUri)
+ break;
+ else if (node.NamespaceURI == JsonNamespaceUri)
+ break;
+
if (writePropertyName)
writer.WritePropertyName(GetPropertyName(node));
writer.WriteValue(node.Value);
@@ -216,6 +253,10 @@ namespace Newtonsoft.Json.Converters XmlDocument document = new XmlDocument();
XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
+
+ if (reader.TokenType != JsonToken.StartObject)
+ throw new JsonSerializationException("XmlNodeConverter can only convert JSON that begins with an object.");
+
reader.Read();
DeserializeNode(reader, document, manager, document);
@@ -241,7 +282,7 @@ namespace Newtonsoft.Json.Converters break;
default:
// processing instructions and the xml declaration start with ?
- if (propertyName[0] == '?')
+ if (!string.IsNullOrEmpty(propertyName) && propertyName[0] == '?')
{
if (propertyName == DeclarationName)
{
@@ -292,7 +333,8 @@ namespace Newtonsoft.Json.Converters && reader.TokenType != JsonToken.Boolean
&& reader.TokenType != JsonToken.Integer
&& reader.TokenType != JsonToken.Float
- && reader.TokenType != JsonToken.Date)
+ && reader.TokenType != JsonToken.Date
+ && reader.TokenType != JsonToken.StartConstructor)
{
// read properties until first non-attribute is encountered
while (!finishedAttributes && !finishedElement && reader.Read())
@@ -370,7 +412,8 @@ namespace Newtonsoft.Json.Converters }
else if (reader.TokenType == JsonToken.Date)
{
- element.AppendChild(document.CreateTextNode(XmlConvert.ToString((DateTime)reader.Value)));
+ DateTime d = (DateTime)reader.Value;
+ element.AppendChild(document.CreateTextNode(XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind))));
}
else if (reader.TokenType == JsonToken.Null)
{
@@ -400,6 +443,9 @@ namespace Newtonsoft.Json.Converters switch (reader.TokenType)
{
case JsonToken.PropertyName:
+ if (currentNode.NodeType == XmlNodeType.Document && document.DocumentElement != null)
+ throw new JsonSerializationException("JSON root object has multiple properties.");
+
string propertyName = reader.Value.ToString();
reader.Read();
@@ -415,6 +461,14 @@ namespace Newtonsoft.Json.Converters 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.EndObject:
case JsonToken.EndArray:
return;
@@ -459,6 +513,16 @@ namespace Newtonsoft.Json.Converters else
return qualifiedName.Substring(0, colonPosition);
}
+
+ private IEnumerable<XmlAttribute> ValueAttributes(XmlAttributeCollection c)
+ {
+ return c.OfType<XmlAttribute>().Where(a => a.NamespaceURI != JsonNamespaceUri);
+ }
+
+ private IEnumerable<XmlNode> ValueNodes(XmlNodeList c)
+ {
+ return c.OfType<XmlNode>().Where(n => n.NamespaceURI != JsonNamespaceUri);
+ }
#endregion
public override bool CanConvert(Type valueType)
diff --git a/Src/Newtonsoft.Json/JavaScriptConvert.cs b/Src/Newtonsoft.Json/JavaScriptConvert.cs index ea1f7df..5bb4638 100644 --- a/Src/Newtonsoft.Json/JavaScriptConvert.cs +++ b/Src/Newtonsoft.Json/JavaScriptConvert.cs @@ -75,7 +75,7 @@ namespace Newtonsoft.Json Null = "null";
Undefined = "undefined";
- InitialJavaScriptDateTicks = (new DateTime(1970, 1, 1)).Ticks;
+ InitialJavaScriptDateTicks = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;
MinimumJavaScriptDate = new DateTime(100, 1, 1);
}
@@ -86,24 +86,53 @@ namespace Newtonsoft.Json /// <returns>A Json string representation of the <see cref="DateTime"/>.</returns>
public static string ToString(DateTime value)
{
+ return ToStringInternal(new DateTimeOffset(value), value.Kind);
+ }
+
+ /// <summary>
+ /// Converts the <see cref="DateTimeOffset"/> to it's JavaScript string representation.
+ /// </summary>
+ /// <param name="value">The value to convert.</param>
+ /// <returns>A Json string representation of the <see cref="DateTimeOffset"/>.</returns>
+ public static string ToString(DateTimeOffset value)
+ {
+ return ToStringInternal(value, DateTimeKind.Local);
+ }
+
+ public static string ToStringInternal(DateTimeOffset value, DateTimeKind kind)
+ {
long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value);
- return "new Date(" + javaScriptTicks + ")";
+ string offset;
+ switch (kind)
+ {
+ case DateTimeKind.Local:
+ case DateTimeKind.Unspecified:
+ TimeSpan utcOffset = value.Offset;
+ offset = utcOffset.Hours.ToString("+00;-00") + utcOffset.Minutes.ToString("00;00");
+ break;
+ default:
+ offset = string.Empty;
+ break;
+ }
+ return @"""\/Date(" + javaScriptTicks.ToString(CultureInfo.InvariantCulture) + offset + @")\/""";
}
- internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime)
+ internal static long ConvertDateTimeToJavaScriptTicks(DateTimeOffset dateTime)
{
- if (dateTime < MinimumJavaScriptDate)
- dateTime = MinimumJavaScriptDate;
+ DateTimeOffset utcDateTime = dateTime.ToUniversalTime();
- long javaScriptTicks = (dateTime.Ticks - InitialJavaScriptDateTicks) / (long)10000;
+ //if (utcDateTime < MinimumJavaScriptDate)
+ // utcDateTime = MinimumJavaScriptDate;
+
+ long javaScriptTicks = (utcDateTime.Ticks - InitialJavaScriptDateTicks) / (long)10000;
return javaScriptTicks;
}
internal static DateTime ConvertJavaScriptTicksToDateTime(long javaScriptTicks)
{
- DateTime dateTime = new DateTime((javaScriptTicks * 10000) + InitialJavaScriptDateTicks);
+ DateTime dateTime = new DateTime((javaScriptTicks * 10000) + InitialJavaScriptDateTicks, DateTimeKind.Utc);
return dateTime;
}
@@ -328,6 +357,10 @@ namespace Newtonsoft.Json return ToString((decimal)convertible);
}
}
+ else if (value is DateTimeOffset)
+ {
+ return ToString((DateTimeOffset)value);
+ }
throw new ArgumentException(string.Format("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.", value.GetType()));
}
@@ -397,12 +430,28 @@ namespace Newtonsoft.Json }
/// <summary>
+ /// Deserializes the specified JSON to the given anonymous type.
+ /// </summary>
+ /// <typeparam name="T">
+ /// The anonymous type to deserialize to. This can't be specified
+ /// traditionally and must be infered from the anonymous type passed
+ /// as a parameter.
+ /// </typeparam>
+ /// <param name="value">The object to deserialize.</param>
+ /// <param name="anonymousTypeObject">The anonymous type object.</param>
+ /// <returns>The deserialized anonymous type from the JSON string.</returns>
+ public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject)
+ {
+ return (T)DeserializeObject(value, typeof(T));
+ }
+
+ /// <summary>
/// Deserializes the specified object to a Json object.
/// </summary>
/// <typeparam name="T">The type of the object to deserialize.</typeparam>
/// <param name="value">The object to deserialize.</param>
/// <param name="converters">Converters to use while deserializing.</param>
- /// <returns>The deserialized object from the Json string.</returns>
+ /// <returns>The deserialized object from the JSON string.</returns>
public static T DeserializeObject<T>(string value, params JsonConverter[] converters)
{
return (T)DeserializeObject(value, typeof(T), converters);
@@ -438,7 +487,7 @@ namespace Newtonsoft.Json return SerializeObject(node, converter);
}
- public static XmlNode DeerializeXmlNode(string value)
+ public static XmlNode DeserializeXmlNode(string value)
{
XmlNodeConverter converter = new XmlNodeConverter();
diff --git a/Src/Newtonsoft.Json/JsonConverter.cs b/Src/Newtonsoft.Json/JsonConverter.cs index bff625b..849f9be 100644 --- a/Src/Newtonsoft.Json/JsonConverter.cs +++ b/Src/Newtonsoft.Json/JsonConverter.cs @@ -40,9 +40,9 @@ namespace Newtonsoft.Json public virtual object ReadJson(JsonReader reader, Type objectType)
{
- throw new NotImplementedException(string.Format("{0} has not overriden FromJson method.", GetType().Name));
+ throw new NotImplementedException(string.Format("{0} has not overriden the ReadJson method.", GetType().Name));
}
public abstract bool CanConvert(Type objectType);
}
-}
+}
\ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index db7181e..8421c2d 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -217,11 +217,54 @@ namespace Newtonsoft.Json ClearCurrentChar();
_currentState = State.PostValue;
- _token = JsonToken.String;
- _value = _buffer.ToString();
+ string text = _buffer.ToString();
_buffer.Position = 0;
- _valueType = typeof(string);
- _quoteChar = quote;
+
+ if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal))
+ {
+ ParseDate(text);
+ }
+ else
+ {
+ SetToken(JsonToken.String, text);
+ _quoteChar = quote;
+ }
+ }
+
+ private void ParseDate(string text)
+ {
+ string value = text.Substring(6, text.Length - 8);
+ DateTimeKind kind = DateTimeKind.Utc;
+
+ int index = value.IndexOf('+', 1);
+
+ if (index == -1)
+ index = value.IndexOf('-', 1);
+
+ if (index != -1)
+ {
+ kind = DateTimeKind.Local;
+ value = value.Substring(0, index);
+ }
+
+ long javaScriptTicks = long.Parse(value);
+ DateTime utcDateTime = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
+ DateTime dateTime;
+
+ switch (kind)
+ {
+ case DateTimeKind.Unspecified:
+ dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
+ break;
+ case DateTimeKind.Local:
+ dateTime = utcDateTime.ToLocalTime();
+ break;
+ default:
+ dateTime = utcDateTime;
+ break;
+ }
+
+ SetToken(JsonToken.Date, dateTime);
}
private bool MoveNext()
@@ -648,22 +691,22 @@ namespace Newtonsoft.Json _value = constructorName;
_valueType = typeof(string);
- if (string.CompareOrdinal(constructorName, "Date") == 0)
- {
- IList<object> parameters = new List<object>();
+ //if (string.CompareOrdinal(constructorName, "Date") == 0)
+ //{
+ // IList<object> parameters = new List<object>();
- MoveNext();
- while (ParseValue() && _token != JsonToken.EndConstructor)
- {
- parameters.Add(_value);
- }
+ // MoveNext();
+ // while (ParseValue() && _token != JsonToken.EndConstructor)
+ // {
+ // parameters.Add(_value);
+ // }
- long javaScriptTicks = Convert.ToInt64(parameters[0]);
+ // long javaScriptTicks = Convert.ToInt64(parameters[0]);
- DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
+ // DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks);
- SetToken(JsonToken.Date, date);
- }
+ // SetToken(JsonToken.Date, date);
+ //}
@@ -754,6 +797,9 @@ namespace Newtonsoft.Json case JsonType.Array:
_currentState = State.Array;
break;
+ case JsonType.Constructor:
+ _currentState = State.Constructor;
+ break;
case JsonType.None:
_currentState = State.Finished;
break;
diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 55243ed..359ed71 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -32,6 +32,7 @@ using System.Reflection; using System.ComponentModel;
using Newtonsoft.Json.Utilities;
using System.Globalization;
+using System.Linq;
namespace Newtonsoft.Json
{
@@ -233,11 +234,7 @@ namespace Newtonsoft.Json case JsonToken.EndConstructor:
string constructorName = reader.Value.ToString();
- //if (string.CompareOrdinal(constructorName, "Date") == 0)
- // value = CreateDate(reader);
- //else
- //TODO: use objectType plus constructor arguments to instantiate object
- value = constructorName;
+ value = constructorName;
break;
case JsonToken.Null:
case JsonToken.Undefined:
@@ -298,12 +295,15 @@ namespace Newtonsoft.Json return targetConverter.ConvertFromInvariantString(valueString);
}
+ if (targetType == typeof(DateTimeOffset) && value is DateTime)
+ return new DateTimeOffset((DateTime)value);
+
if (!targetType.IsAssignableFrom(valueType))
throw new InvalidOperationException(string.Format("Cannot convert object of type '{0}' to type '{1}'", value.GetType(), targetType));
return value;
}
-
+
return targetConverter.ConvertFrom(null, CultureInfo.InvariantCulture, value);
}
else
@@ -343,7 +343,7 @@ namespace Newtonsoft.Json else
mappedName = member.Name;
- bool ignored = member.IsDefined(typeof (JsonIgnoreAttribute), true);
+ bool ignored = member.IsDefined(typeof(JsonIgnoreAttribute), true);
bool readable = ReflectionUtils.CanReadMemberValue(member);
bool writable = ReflectionUtils.CanSetMemberValue(member);
MemberMapping memberMapping = new MemberMapping(mappedName, member, ignored, readable, writable);
@@ -409,50 +409,102 @@ namespace Newtonsoft.Json Type elementType = ReflectionUtils.GetListItemType(objectType);
IList populatedList = CollectionUtils.CreateAndPopulateList(objectType, delegate(IList list)
+ {
+ while (reader.Read())
{
- while (reader.Read())
+ switch (reader.TokenType)
{
- switch (reader.TokenType)
- {
- case JsonToken.EndArray:
- return;
- case JsonToken.Comment:
- break;
- default:
- object value = GetObject(reader, elementType);
-
- list.Add(value);
- break;
- }
+ case JsonToken.EndArray:
+ return;
+ case JsonToken.Comment:
+ break;
+ default:
+ object value = GetObject(reader, elementType);
+
+ list.Add(value);
+ break;
}
+ }
- throw new JsonSerializationException("Unexpected end when deserializing array.");
- });
+ throw new JsonSerializationException("Unexpected end when deserializing array.");
+ });
return populatedList;
}
private object PopulateObject(JsonReader reader, Type objectType)
{
- object newObject = Activator.CreateInstance(objectType);
+ object newObject;
- while (reader.Read())
+ if (ReflectionUtils.HasDefaultConstructor(objectType))
{
- switch (reader.TokenType)
+ newObject = Activator.CreateInstance(objectType);
+
+ while (reader.Read())
{
- case JsonToken.PropertyName:
- string memberName = reader.Value.ToString();
+ switch (reader.TokenType)
+ {
+ case JsonToken.PropertyName:
+ string memberName = reader.Value.ToString();
- SetObjectMember(reader, newObject, objectType, memberName);
- break;
- case JsonToken.EndObject:
- return newObject;
- default:
- throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
+ SetObjectMember(reader, newObject, objectType, memberName);
+ break;
+ case JsonToken.EndObject:
+ return newObject;
+ default:
+ throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
+ }
}
+
+ throw new JsonSerializationException("Unexpected end when deserializing object.");
}
+ else
+ {
+ ConstructorInfo c = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault();
+
+ if (c == null)
+ throw new Exception("sdsdf");
+
+ IDictionary<ParameterInfo, object> constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null);
+ //IList<object> constructorValues
+
+ bool exit = false;
+ while (!exit && reader.Read())
+ {
+ switch (reader.TokenType)
+ {
+ case JsonToken.PropertyName:
+ string memberName = reader.Value.ToString();
+ ParameterInfo matchingConstructorParameter = constructorParameters
+ .Where(kv => kv.Key.Name == memberName)
+ .Select(kv => kv.Key)
+ .SingleOrDefault();
+
+ if (!reader.Read())
+ throw new JsonSerializationException(string.Format("Unexpected end when setting {0}'s value.", memberName));
+
+
+ if (matchingConstructorParameter != null)
+ {
+ constructorParameters[matchingConstructorParameter] = GetObject(reader, matchingConstructorParameter.ParameterType);
+ }
+
- throw new JsonSerializationException("Unexpected end when deserializing object.");
+
+ //SetObjectMember(reader, newObject, objectType, memberName);
+ break;
+ case JsonToken.EndObject:
+ exit = true;
+ break;
+ default:
+ throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
+ }
+ }
+
+ newObject = Activator.CreateInstance(objectType, constructorParameters.Values.ToArray());
+
+ return newObject;
+ }
}
#endregion
@@ -554,6 +606,10 @@ namespace Newtonsoft.Json break;
}
}
+ else if (value is DateTimeOffset)
+ {
+ writer.WriteValue((DateTimeOffset)value);
+ }
else if (value is IList)
{
SerializeList(writer, (IList)value);
@@ -694,4 +750,4 @@ namespace Newtonsoft.Json }
#endregion
}
-}
+}
\ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 953f198..f488e6b 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -724,6 +724,15 @@ namespace Newtonsoft.Json {
WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date);
}
+
+ /// <summary>
+ /// Writes a <see cref="DateTimeOffset"/> value.
+ /// </summary>
+ /// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
+ public void WriteValue(DateTimeOffset value)
+ {
+ WriteValueInternal(JavaScriptConvert.ToString(value), JsonToken.Date);
+ }
#endregion
/// <summary>
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 51fb364..530039e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -73,7 +73,9 @@ <Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
- <Compile Include="Converters\AspNetAjaxDateTimeConverter.cs" />
+ <Compile Include="Converters\IsoDateTimeConverter.cs" />
+ <Compile Include="Converters\JavaScriptDateTimeConverter.cs" />
+ <Compile Include="Converters\JsonDateTimeSerializationMode.cs" />
<Compile Include="Converters\XmlNodeConverter.cs" />
<Compile Include="Converters\HtmlColorConverter.cs" />
<Compile Include="JavaScriptParameters.cs" />
@@ -105,6 +107,7 @@ <Compile Include="MissingMemberHandling.cs" />
<Compile Include="NullValueHandling.cs" />
<Compile Include="ReferenceLoopHandling.cs" />
+ <Compile Include="Utilities\DateTimeUtils.cs" />
<Compile Include="Utilities\JavaScriptUtils.cs" />
<Compile Include="JsonToken.cs" />
<Compile Include="JsonWriter.cs" />
diff --git a/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs b/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs new file mode 100644 index 0000000..4ba824a --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs @@ -0,0 +1,33 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+
+namespace Newtonsoft.Json.Utilities
+{
+ public static class DateTimeUtils
+ {
+ public static string GetLocalOffset(this DateTime d)
+ {
+ TimeSpan utcOffset = TimeZoneInfo.Local.GetUtcOffset(d);
+
+ return utcOffset.Hours.ToString("+00;-00") + ":" + utcOffset.Minutes.ToString("00;00");
+ }
+
+ 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 new ArgumentOutOfRangeException("kind", kind, "Unexpected DateTimeKind value.");
+ }
+ }
+ }
+}
diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index c6084c3..79a2170 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -545,4 +545,4 @@ namespace Newtonsoft.Json.Utilities return Activator.CreateInstance(specificType, args);
}
}
-}
+}
\ No newline at end of file |