826 lines
29 KiB
C#
826 lines
29 KiB
C#
#region License
|
|
// Copyright (c) 2007 James Newton-King
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person
|
|
// obtaining a copy of this software and associated documentation
|
|
// files (the "Software"), to deal in the Software without
|
|
// restriction, including without limitation the rights to use,
|
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following
|
|
// conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
// OTHER DEALINGS IN THE SOFTWARE.
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Reflection;
|
|
using System.ComponentModel;
|
|
using Newtonsoft.Json.Serialization;
|
|
using Newtonsoft.Json.Utilities;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Collections.ObjectModel;
|
|
|
|
namespace Newtonsoft.Json
|
|
{
|
|
/// <summary>
|
|
/// Serializes and deserializes objects into and from the Json format.
|
|
/// The <see cref="JsonSerializer"/> enables you to control how objects are encoded into Json.
|
|
/// </summary>
|
|
public class JsonSerializer
|
|
{
|
|
#region Properties
|
|
private ReferenceLoopHandling _referenceLoopHandling;
|
|
private MissingMemberHandling _missingMemberHandling;
|
|
private ObjectCreationHandling _objectCreationHandling;
|
|
private NullValueHandling _nullValueHandling;
|
|
private DefaultValueHandling _defaultValueHandling;
|
|
private int _level;
|
|
private JsonConverterCollection _converters;
|
|
|
|
/// <summary>
|
|
/// Get or set how reference loops (e.g. a class referencing itself) is handled.
|
|
/// </summary>
|
|
public ReferenceLoopHandling ReferenceLoopHandling
|
|
{
|
|
get { return _referenceLoopHandling; }
|
|
set
|
|
{
|
|
if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_referenceLoopHandling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization.
|
|
/// </summary>
|
|
public MissingMemberHandling MissingMemberHandling
|
|
{
|
|
get { return _missingMemberHandling; }
|
|
set
|
|
{
|
|
if (value < MissingMemberHandling.Ignore || value > MissingMemberHandling.Error)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_missingMemberHandling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or set how null values are handled during serialization and deserialization.
|
|
/// </summary>
|
|
public NullValueHandling NullValueHandling
|
|
{
|
|
get { return _nullValueHandling; }
|
|
set
|
|
{
|
|
if (value < NullValueHandling.Include || value > NullValueHandling.Ignore)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_nullValueHandling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get or set how null default are handled during serialization and deserialization.
|
|
/// </summary>
|
|
public DefaultValueHandling DefaultValueHandling
|
|
{
|
|
get { return _defaultValueHandling; }
|
|
set
|
|
{
|
|
if (value < DefaultValueHandling.Include || value > DefaultValueHandling.Ignore)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_defaultValueHandling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets how objects are created during deserialization.
|
|
/// </summary>
|
|
/// <value>The object creation handling.</value>
|
|
public ObjectCreationHandling ObjectCreationHandling
|
|
{
|
|
get { return _objectCreationHandling; }
|
|
set
|
|
{
|
|
if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace)
|
|
throw new ArgumentOutOfRangeException("value");
|
|
|
|
_objectCreationHandling = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a collection <see cref="JsonConverter"/> that will be used during serialization.
|
|
/// </summary>
|
|
/// <value>Collection <see cref="JsonConverter"/> that will be used during serialization.</value>
|
|
public JsonConverterCollection Converters
|
|
{
|
|
get
|
|
{
|
|
if (_converters == null)
|
|
_converters = new JsonConverterCollection();
|
|
|
|
return _converters;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="JsonSerializer"/> class.
|
|
/// </summary>
|
|
public JsonSerializer()
|
|
{
|
|
_referenceLoopHandling = JsonSerializerSettings.DefaultReferenceLoopHandling;
|
|
_missingMemberHandling = JsonSerializerSettings.DefaultMissingMemberHandling;
|
|
_nullValueHandling = JsonSerializerSettings.DefaultNullValueHandling;
|
|
_defaultValueHandling = JsonSerializerSettings.DefaultDefaultValueHandling;
|
|
_objectCreationHandling = JsonSerializerSettings.DefaultObjectCreationHandling;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="JsonSerializer"/> instance using the specified <see cref="JsonSerializerSettings"/> objects.
|
|
/// </summary>
|
|
/// <param name="settings">The settings.</param>
|
|
/// <returns></returns>
|
|
public static JsonSerializer Create(JsonSerializerSettings settings)
|
|
{
|
|
JsonSerializer jsonSerializer = new JsonSerializer();
|
|
|
|
if (settings != null)
|
|
{
|
|
if (!CollectionUtils.IsNullOrEmpty<JsonConverter>(settings.Converters))
|
|
jsonSerializer.Converters.AddRange(settings.Converters);
|
|
|
|
jsonSerializer.ReferenceLoopHandling = settings.ReferenceLoopHandling;
|
|
jsonSerializer.MissingMemberHandling = settings.MissingMemberHandling;
|
|
jsonSerializer.ObjectCreationHandling = settings.ObjectCreationHandling;
|
|
jsonSerializer.NullValueHandling = settings.NullValueHandling;
|
|
jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling;
|
|
}
|
|
|
|
return jsonSerializer;
|
|
}
|
|
|
|
#region Deserialize
|
|
/// <summary>
|
|
/// Deserializes the Json structure contained by the specified <see cref="JsonReader"/>.
|
|
/// </summary>
|
|
/// <param name="reader">The <see cref="JsonReader"/> that contains the Json structure to deserialize.</param>
|
|
/// <returns>The <see cref="Object"/> being deserialized.</returns>
|
|
public object Deserialize(JsonReader reader)
|
|
{
|
|
return Deserialize(reader, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes the Json structure contained by the specified <see cref="JsonReader"/>
|
|
/// into an instance of the specified type.
|
|
/// </summary>
|
|
/// <param name="reader">The <see cref="JsonReader"/> containing the object.</param>
|
|
/// <param name="objectType">The <see cref="Type"/> of object being deserialized.</param>
|
|
/// <returns>The instance of <paramref name="objectType"/> being deserialized.</returns>
|
|
public object Deserialize(JsonReader reader, Type objectType)
|
|
{
|
|
if (reader == null)
|
|
throw new ArgumentNullException("reader");
|
|
|
|
if (!reader.Read())
|
|
return null;
|
|
|
|
if (objectType != null)
|
|
return CreateObject(reader, objectType, null, null);
|
|
else
|
|
return CreateJToken(reader);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deserializes the Json structure contained by the specified <see cref="StringReader"/>
|
|
/// into an instance of the specified type.
|
|
/// </summary>
|
|
/// <param name="reader">The <see cref="TextReader"/> containing the object.</param>
|
|
/// <param name="objectType">The <see cref="Type"/> of object being deserialized.</param>
|
|
/// <returns>The instance of <paramref name="objectType"/> being deserialized.</returns>
|
|
public object Deserialize(TextReader reader, Type objectType)
|
|
{
|
|
return Deserialize(new JsonTextReader(reader), objectType);
|
|
}
|
|
|
|
private JToken CreateJToken(JsonReader reader)
|
|
{
|
|
JToken token;
|
|
using (JsonTokenWriter writer = new JsonTokenWriter())
|
|
{
|
|
writer.WriteToken(reader);
|
|
token = writer.Token;
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
private bool HasClassConverter(Type objectType, out JsonConverter converter)
|
|
{
|
|
if (objectType == null)
|
|
throw new ArgumentNullException("objectType");
|
|
|
|
converter = JsonTypeReflector.GetConverter(objectType, objectType);
|
|
return (converter != null);
|
|
}
|
|
|
|
private object CreateObject(JsonReader reader, Type objectType, object existingValue, JsonConverter memberConverter)
|
|
{
|
|
_level++;
|
|
|
|
object value;
|
|
JsonConverter converter;
|
|
|
|
if (memberConverter != null)
|
|
{
|
|
return memberConverter.ReadJson(reader, objectType);
|
|
}
|
|
else if (objectType != null && HasClassConverter(objectType, out converter))
|
|
{
|
|
return converter.ReadJson(reader, objectType);
|
|
}
|
|
else if (objectType != null && HasMatchingConverter(objectType, out converter))
|
|
{
|
|
return converter.ReadJson(reader, objectType);
|
|
}
|
|
else if (objectType == typeof(JsonRaw))
|
|
{
|
|
return JsonRaw.Create(reader);
|
|
}
|
|
else
|
|
{
|
|
switch (reader.TokenType)
|
|
{
|
|
// populate a typed object or generic dictionary/array
|
|
// depending upon whether an objectType was supplied
|
|
case JsonToken.StartObject:
|
|
if (objectType == null)
|
|
{
|
|
value = CreateJToken(reader);
|
|
}
|
|
else if (CollectionUtils.IsDictionaryType(objectType))
|
|
{
|
|
if (existingValue == null)
|
|
value = CreateAndPopulateDictionary(reader, objectType);
|
|
else
|
|
value = PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(existingValue), reader);
|
|
}
|
|
else
|
|
{
|
|
if (existingValue == null)
|
|
value = CreateAndPopulateObject(reader, objectType);
|
|
else
|
|
value = PopulateObject(existingValue, reader, objectType);
|
|
}
|
|
break;
|
|
case JsonToken.StartArray:
|
|
if (objectType != null)
|
|
{
|
|
if (existingValue == null)
|
|
value = CreateAndPopulateList(reader, objectType);
|
|
else
|
|
value = PopulateList(CollectionUtils.CreateCollectionWrapper(existingValue), ReflectionUtils.GetCollectionItemType(objectType), reader);
|
|
}
|
|
else
|
|
{
|
|
value = CreateJToken(reader);
|
|
}
|
|
break;
|
|
case JsonToken.Integer:
|
|
case JsonToken.Float:
|
|
case JsonToken.String:
|
|
case JsonToken.Boolean:
|
|
case JsonToken.Date:
|
|
value = EnsureType(reader.Value, objectType);
|
|
break;
|
|
case JsonToken.StartConstructor:
|
|
case JsonToken.EndConstructor:
|
|
string constructorName = reader.Value.ToString();
|
|
|
|
value = constructorName;
|
|
break;
|
|
case JsonToken.Null:
|
|
case JsonToken.Undefined:
|
|
if (objectType == typeof(DBNull))
|
|
value = DBNull.Value;
|
|
else
|
|
value = null;
|
|
break;
|
|
default:
|
|
throw new JsonSerializationException("Unexpected token while deserializing object: " + reader.TokenType);
|
|
}
|
|
}
|
|
|
|
_level--;
|
|
|
|
return value;
|
|
}
|
|
|
|
private object EnsureType(object value, Type targetType)
|
|
{
|
|
// do something about null value when the targetType is a valuetype?
|
|
if (value == null)
|
|
return null;
|
|
|
|
if (targetType == null)
|
|
return value;
|
|
|
|
Type valueType = value.GetType();
|
|
|
|
// type of value and type of target don't match
|
|
// attempt to convert value's type to target's type
|
|
if (valueType != targetType)
|
|
{
|
|
return ConvertUtils.ConvertOrCast(value, CultureInfo.InvariantCulture, targetType);
|
|
}
|
|
else
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
protected virtual JsonMemberMappingCollection GetMemberMappings(Type objectType)
|
|
{
|
|
ValidationUtils.ArgumentNotNull(objectType, "objectType");
|
|
|
|
return JsonTypeReflector.GetMemberMappings(objectType);
|
|
}
|
|
|
|
private void SetObjectMember(JsonReader reader, object target, Type targetType, string memberName)
|
|
{
|
|
JsonMemberMappingCollection memberMappings = GetMemberMappings(targetType);
|
|
Type memberType;
|
|
object value;
|
|
|
|
// test if a member with memberName exists on the type
|
|
// otherwise test if target is a dictionary and assign value with the key if it is
|
|
if (memberMappings.Contains(memberName))
|
|
{
|
|
JsonMemberMapping memberMapping = memberMappings[memberName];
|
|
|
|
if (memberMapping.Ignored)
|
|
{
|
|
reader.Skip();
|
|
return;
|
|
}
|
|
|
|
// get the member's underlying type
|
|
memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member);
|
|
|
|
object currentValue = null;
|
|
bool useExistingValue = false;
|
|
|
|
if ((_objectCreationHandling == ObjectCreationHandling.Auto || _objectCreationHandling == ObjectCreationHandling.Reuse)
|
|
&& (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject))
|
|
{
|
|
currentValue = ReflectionUtils.GetMemberValue(memberMapping.Member, target);
|
|
|
|
useExistingValue = (currentValue != null && !memberType.IsArray && !ReflectionUtils.InheritsGenericDefinition(memberType, typeof(ReadOnlyCollection<>)));
|
|
}
|
|
|
|
|
|
if (!memberMapping.Writable && !useExistingValue)
|
|
{
|
|
reader.Skip();
|
|
return;
|
|
}
|
|
|
|
value = CreateObject(reader, memberType, (useExistingValue) ? currentValue : null, JsonTypeReflector.GetConverter(memberMapping.Member, memberType));
|
|
|
|
if (_nullValueHandling == NullValueHandling.Ignore && value == null)
|
|
return;
|
|
|
|
if (_defaultValueHandling == DefaultValueHandling.Ignore && object.Equals(value, memberMapping.DefaultValue))
|
|
return;
|
|
|
|
if (!useExistingValue)
|
|
ReflectionUtils.SetMemberValue(memberMapping.Member, target, value);
|
|
}
|
|
else
|
|
{
|
|
if (_missingMemberHandling == MissingMemberHandling.Error)
|
|
throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, targetType.Name));
|
|
|
|
reader.Skip();
|
|
}
|
|
}
|
|
|
|
private object CreateAndPopulateDictionary(JsonReader reader, Type objectType)
|
|
{
|
|
IWrappedDictionary dictionary = CollectionUtils.CreateDictionaryWrapper(Activator.CreateInstance(objectType));
|
|
PopulateDictionary(dictionary, reader);
|
|
|
|
return dictionary.UnderlyingDictionary;
|
|
}
|
|
|
|
private IDictionary PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader)
|
|
{
|
|
Type dictionaryType = dictionary.UnderlyingDictionary.GetType();
|
|
Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryType);
|
|
Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryType);
|
|
|
|
while (reader.Read())
|
|
{
|
|
switch (reader.TokenType)
|
|
{
|
|
case JsonToken.PropertyName:
|
|
object keyValue = EnsureType(reader.Value, dictionaryKeyType);
|
|
reader.Read();
|
|
|
|
dictionary.Add(keyValue, CreateObject(reader, dictionaryValueType, null, null));
|
|
break;
|
|
case JsonToken.EndObject:
|
|
return dictionary;
|
|
default:
|
|
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
|
}
|
|
}
|
|
|
|
throw new JsonSerializationException("Unexpected end when deserializing object.");
|
|
}
|
|
|
|
private object CreateAndPopulateList(JsonReader reader, Type objectType)
|
|
{
|
|
return CollectionUtils.CreateAndPopulateList(objectType, l => PopulateList(l, ReflectionUtils.GetCollectionItemType(objectType), reader));
|
|
}
|
|
|
|
private IList PopulateList(IList list, Type listItemType, JsonReader reader)
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
switch (reader.TokenType)
|
|
{
|
|
case JsonToken.EndArray:
|
|
return list;
|
|
case JsonToken.Comment:
|
|
break;
|
|
default:
|
|
object value = CreateObject(reader, listItemType, null, null);
|
|
|
|
list.Add(value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
throw new JsonSerializationException("Unexpected end when deserializing array.");
|
|
}
|
|
|
|
private object CreateAndPopulateObject(JsonReader reader, Type objectType)
|
|
{
|
|
object newObject;
|
|
|
|
if (objectType.IsInterface || objectType.IsAbstract)
|
|
{
|
|
throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
|
}
|
|
else if (ReflectionUtils.HasDefaultConstructor(objectType))
|
|
{
|
|
newObject = Activator.CreateInstance(objectType);
|
|
|
|
PopulateObject(newObject, reader, objectType);
|
|
return newObject;
|
|
}
|
|
else
|
|
{
|
|
ConstructorInfo c = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault();
|
|
|
|
if (c == null)
|
|
throw new JsonSerializationException("Could not find a public constructor for type {0}.".FormatWith(CultureInfo.InvariantCulture, objectType));
|
|
|
|
// create a dictionary to put retrieved values into
|
|
IDictionary<ParameterInfo, object> constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null);
|
|
JsonMemberMappingCollection mappings = GetMemberMappings(objectType);
|
|
|
|
bool exit = false;
|
|
while (!exit && reader.Read())
|
|
{
|
|
switch (reader.TokenType)
|
|
{
|
|
case JsonToken.PropertyName:
|
|
string memberName = reader.Value.ToString();
|
|
if (!reader.Read())
|
|
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
|
|
|
ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, memberName).Key;
|
|
JsonMemberMapping mapping;
|
|
|
|
if (matchingConstructorParameter != null && mappings.TryGetMapping(memberName, out mapping) && !mapping.Ignored)
|
|
{
|
|
constructorParameters[matchingConstructorParameter] = CreateObject(reader, matchingConstructorParameter.ParameterType, null, mapping.MemberConverter);
|
|
}
|
|
else
|
|
{
|
|
// skip token and all child tokens
|
|
reader.Skip();
|
|
}
|
|
break;
|
|
case JsonToken.EndObject:
|
|
exit = true;
|
|
break;
|
|
default:
|
|
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
|
}
|
|
}
|
|
|
|
newObject = ReflectionUtils.CreateInstance(objectType, constructorParameters.Values.ToArray());
|
|
|
|
return newObject;
|
|
}
|
|
}
|
|
|
|
private object PopulateObject(object newObject, JsonReader reader, Type objectType)
|
|
{
|
|
JsonMemberMappingCollection memberMappings = GetMemberMappings(objectType);
|
|
Dictionary<string, bool> requiredMappings =
|
|
memberMappings.Where(m => m.Required).ToDictionary(m => m.MappingName, m => false);
|
|
//List<RequiredMemberContext> requiredMappingContexts =
|
|
// memberMappings.Where(m => m.Required).Select(m => new RequiredMemberContext(m.MappingName, false)).ToList();
|
|
|
|
//ObjectContext context = new ObjectContext(newObject, requiredMappingContexts);
|
|
//DeserializeStack.Add(context);
|
|
|
|
while (reader.Read())
|
|
{
|
|
switch (reader.TokenType)
|
|
{
|
|
case JsonToken.PropertyName:
|
|
string memberName = reader.Value.ToString();
|
|
|
|
if (!reader.Read())
|
|
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
|
|
|
if (reader.TokenType != JsonToken.Null)
|
|
{
|
|
if (requiredMappings.ContainsKey(memberName))
|
|
requiredMappings[memberName] = true;
|
|
}
|
|
|
|
SetObjectMember(reader, newObject, objectType, memberName);
|
|
break;
|
|
case JsonToken.EndObject:
|
|
foreach (KeyValuePair<string, bool> requiredMapping in requiredMappings)
|
|
{
|
|
if (!requiredMapping.Value)
|
|
throw new JsonSerializationException("Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, requiredMapping.Key));
|
|
}
|
|
// TODO: move below, change to RemoveAt
|
|
//DeserializeStack.Remove(context);
|
|
return newObject;
|
|
default:
|
|
throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType);
|
|
}
|
|
}
|
|
|
|
throw new JsonSerializationException("Unexpected end when deserializing object.");
|
|
}
|
|
#endregion
|
|
|
|
#region Serialize
|
|
/// <summary>
|
|
/// Serializes the specified <see cref="Object"/> and writes the Json structure
|
|
/// to a <c>Stream</c> using the specified <see cref="TextWriter"/>.
|
|
/// </summary>
|
|
/// <param name="textWriter">The <see cref="TextWriter"/> used to write the Json structure.</param>
|
|
/// <param name="value">The <see cref="Object"/> to serialize.</param>
|
|
public void Serialize(TextWriter textWriter, object value)
|
|
{
|
|
Serialize(new JsonTextWriter(textWriter), value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes the specified <see cref="Object"/> and writes the Json structure
|
|
/// to a <c>Stream</c> using the specified <see cref="JsonWriter"/>.
|
|
/// </summary>
|
|
/// <param name="jsonWriter">The <see cref="JsonWriter"/> used to write the Json structure.</param>
|
|
/// <param name="value">The <see cref="Object"/> to serialize.</param>
|
|
public void Serialize(JsonWriter jsonWriter, object value)
|
|
{
|
|
if (jsonWriter == null)
|
|
throw new ArgumentNullException("jsonWriter");
|
|
|
|
if (value is JToken)
|
|
((JToken)value).WriteTo(jsonWriter, (_converters != null) ? _converters.ToArray() : null);
|
|
else
|
|
SerializeValue(jsonWriter, value, null);
|
|
}
|
|
|
|
|
|
private void SerializeValue(JsonWriter writer, object value, JsonConverter memberConverter)
|
|
{
|
|
JsonConverter converter;
|
|
|
|
if (value == null)
|
|
{
|
|
writer.WriteNull();
|
|
}
|
|
else if (memberConverter != null)
|
|
{
|
|
memberConverter.WriteJson(writer, value);
|
|
}
|
|
else if (HasClassConverter(value.GetType(), out converter))
|
|
{
|
|
converter.WriteJson(writer, value);
|
|
}
|
|
else if (HasMatchingConverter(value.GetType(), out converter))
|
|
{
|
|
converter.WriteJson(writer, value);
|
|
}
|
|
else if (JsonConvert.IsJsonPrimitive(value))
|
|
{
|
|
writer.WriteValue(value);
|
|
}
|
|
else if (value is IList)
|
|
{
|
|
SerializeList(writer, (IList)value);
|
|
}
|
|
else if (value is IDictionary)
|
|
{
|
|
SerializeDictionary(writer, (IDictionary)value);
|
|
}
|
|
else if (value is ICollection)
|
|
{
|
|
SerializeCollection(writer, (ICollection)value);
|
|
}
|
|
else if (value is IEnumerable)
|
|
{
|
|
SerializeEnumerable(writer, (IEnumerable)value);
|
|
}
|
|
else if (value is JsonRaw)
|
|
{
|
|
writer.WriteRawValue(((JsonRaw)value).Content);
|
|
}
|
|
else
|
|
{
|
|
SerializeObject(writer, value);
|
|
}
|
|
}
|
|
|
|
private bool HasMatchingConverter(Type type, out JsonConverter matchingConverter)
|
|
{
|
|
return HasMatchingConverter(_converters, type, out matchingConverter);
|
|
}
|
|
|
|
internal static bool HasMatchingConverter(IList<JsonConverter> converters, Type objectType, out JsonConverter matchingConverter)
|
|
{
|
|
if (objectType == null)
|
|
throw new ArgumentNullException("objectType");
|
|
|
|
if (converters != null)
|
|
{
|
|
for (int i = 0; i < converters.Count; i++)
|
|
{
|
|
JsonConverter converter = converters[i];
|
|
|
|
if (converter.CanConvert(objectType))
|
|
{
|
|
matchingConverter = converter;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
matchingConverter = null;
|
|
return false;
|
|
}
|
|
|
|
private void WriteMemberInfoProperty(JsonWriter writer, object value, JsonMemberMapping memberMapping)
|
|
{
|
|
MemberInfo member = memberMapping.Member;
|
|
string propertyName = memberMapping.MappingName;
|
|
JsonConverter memberConverter = memberMapping.MemberConverter;
|
|
object defaultValue = memberMapping.DefaultValue;
|
|
|
|
if (!ReflectionUtils.IsIndexedProperty(member))
|
|
{
|
|
object memberValue = ReflectionUtils.GetMemberValue(member, value);
|
|
|
|
if (_nullValueHandling == NullValueHandling.Ignore && memberValue == null)
|
|
return;
|
|
|
|
if (_defaultValueHandling == DefaultValueHandling.Ignore && object.Equals(memberValue, defaultValue))
|
|
return;
|
|
|
|
if (writer.SerializeStack.IndexOf(memberValue) != -1)
|
|
{
|
|
switch (_referenceLoopHandling)
|
|
{
|
|
case ReferenceLoopHandling.Error:
|
|
throw new JsonSerializationException("Self referencing loop");
|
|
case ReferenceLoopHandling.Ignore:
|
|
// return from method
|
|
return;
|
|
case ReferenceLoopHandling.Serialize:
|
|
// continue
|
|
break;
|
|
default:
|
|
throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, _referenceLoopHandling));
|
|
}
|
|
}
|
|
|
|
writer.WritePropertyName(propertyName ?? member.Name);
|
|
SerializeValue(writer, memberValue, memberConverter);
|
|
}
|
|
}
|
|
|
|
private void SerializeObject(JsonWriter writer, object value)
|
|
{
|
|
Type objectType = value.GetType();
|
|
|
|
#if !SILVERLIGHT && !PocketPC
|
|
TypeConverter converter = TypeDescriptor.GetConverter(objectType);
|
|
|
|
// 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) || value is Type))
|
|
{
|
|
if (converter.CanConvertTo(typeof(string)))
|
|
{
|
|
writer.WriteValue(converter.ConvertToInvariantString(value));
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
if (value is Guid || value is Type)
|
|
{
|
|
writer.WriteValue(value.ToString());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
writer.SerializeStack.Add(value);
|
|
|
|
writer.WriteStartObject();
|
|
|
|
JsonMemberMappingCollection memberMappings = GetMemberMappings(objectType);
|
|
|
|
foreach (JsonMemberMapping memberMapping in memberMappings)
|
|
{
|
|
if (!memberMapping.Ignored && memberMapping.Readable)
|
|
WriteMemberInfoProperty(writer, value, memberMapping);
|
|
}
|
|
|
|
writer.WriteEndObject();
|
|
|
|
writer.SerializeStack.RemoveAt(writer.SerializeStack.Count - 1);
|
|
}
|
|
|
|
private void SerializeEnumerable(JsonWriter writer, IEnumerable values)
|
|
{
|
|
SerializeList(writer, values.Cast<object>().ToList());
|
|
}
|
|
|
|
private void SerializeCollection(JsonWriter writer, ICollection values)
|
|
{
|
|
SerializeList(writer, values.Cast<object>().ToList());
|
|
}
|
|
|
|
private void SerializeList(JsonWriter writer, IList values)
|
|
{
|
|
writer.WriteStartArray();
|
|
|
|
for (int i = 0; i < values.Count; i++)
|
|
{
|
|
SerializeValue(writer, values[i], null);
|
|
}
|
|
|
|
writer.WriteEndArray();
|
|
}
|
|
|
|
private void SerializeDictionary(JsonWriter writer, IDictionary values)
|
|
{
|
|
writer.WriteStartObject();
|
|
|
|
foreach (DictionaryEntry entry in values)
|
|
{
|
|
writer.WritePropertyName(entry.Key.ToString());
|
|
SerializeValue(writer, entry.Value, null);
|
|
}
|
|
|
|
writer.WriteEndObject();
|
|
}
|
|
#endregion
|
|
}
|
|
} |