Anonymous avatar Anonymous committed 4b35f0a

Overrode JsonObject Add() method to allow nulls
Added testing for Extension methods
Added ability to specify default types for abstract types and interfaces
Bugs fixed
- implicit conversions do not take nullables
- serializing abstract types does not add type name

Comments (0)

Files changed (18)

 Json.5.1.ReSharper.user
 Json.6.0.ReSharper.user
 Json.sln.DotSettings.user
+NuGet/

Json.Tests/Json/Extensions/JsonExtensionsTest.cs

+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+using Manatee.Json;
+using Manatee.Json.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Tests.Json.Extensions
+{
+	[TestClass]
+	public class JsonExtensionsTest
+	{
+		[TestMethod]
+		public void TryGetString_ReturnsRequested()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+
+			var value = json.TryGetString("string");
+
+			Assert.AreEqual("test", value);
+		}
+		[TestMethod]
+		public void TryGetString_NullObjectReturnsNull()
+		{
+			JsonObject json = null;
+
+			var value = json.TryGetString("string");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetString_ValueNotFoundReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+			var value = json.TryGetString("string");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetString_ValueNotStringReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+			var value = json.TryGetString("number");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetNumber_ReturnsRequested()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+
+			var value = json.TryGetNumber("number");
+
+			Assert.AreEqual(6.0, value);
+		}
+		[TestMethod]
+		public void TryGetNumber_NullObjectReturnsNull()
+		{
+			JsonObject json = null;
+
+			var value = json.TryGetNumber("number");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetNumber_ValueNotFoundReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"boolean", true}
+			           	};
+			var value = json.TryGetNumber("number");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetNumber_ValueNotNumberReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+			var value = json.TryGetNumber("string");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetBoolean_ReturnsRequested()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+
+			var value = json.TryGetBoolean("boolean");
+
+			Assert.AreEqual(true, value);
+		}
+		[TestMethod]
+		public void TryGetBoolean_NullObjectReturnsNull()
+		{
+			JsonObject json = null;
+
+			var value = json.TryGetBoolean("boolean");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetBoolean_ValueNotFoundReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6}
+			           	};
+			var value = json.TryGetBoolean("boolean");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetBoolean_ValueNotBooleanReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"boolean", true}
+			           	};
+			var value = json.TryGetBoolean("string");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetArray_ReturnsRequested()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"array", new JsonArray()}
+			           	};
+
+			var value = json.TryGetArray("array");
+
+			Assert.AreEqual(new JsonArray(), value);
+		}
+		[TestMethod]
+		public void TryGetArray_NullObjectReturnsNull()
+		{
+			JsonObject json = null;
+
+			var value = json.TryGetArray("array");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetArray_ValueNotFoundReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6}
+			           	};
+			var value = json.TryGetArray("array");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetArray_ValueNotBooleanReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"array", new JsonArray()}
+			           	};
+			var value = json.TryGetArray("string");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetObject_ReturnsRequested()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"object", new JsonObject()}
+			           	};
+
+			var value = json.TryGetObject("object");
+
+			Assert.AreEqual(new JsonObject(), value);
+		}
+		[TestMethod]
+		public void TryGetObject_NullObjectReturnsNull()
+		{
+			JsonObject json = null;
+
+			var value = json.TryGetObject("object");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetObject_ValueNotFoundReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6}
+			           	};
+			var value = json.TryGetObject("object");
+
+			Assert.IsNull(value);
+		}
+		[TestMethod]
+		public void TryGetObject_ValueNotBooleanReturnsNull()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"string", "test"},
+			           		{"number", 6},
+			           		{"object", new JsonObject()}
+			           	};
+			var value = json.TryGetObject("string");
+
+			Assert.IsNull(value);
+		}
+	}
+}

Json.Tests/Json/JsonObjectTest.cs

 			var actual = new JsonObject(s);
 		}
 		[TestMethod]
-		public void Parse_StringFromSourceForge()
+		public void Parse_StringFromSourceForge_kheimric()
 		{
 			var s = @"{
   ""self"": ""self"",
 			var actual = new JsonObject(s);
 			var newString = actual.ToString();
 		}
+		[TestMethod]
+		public void Add_NullValueAddsJsonNull()
+		{
+			var obj = new JsonObject();
+			obj.Add("null", null);
+
+			Assert.AreEqual(1, obj.Count);
+			Assert.AreEqual(JsonValue.Null, obj["null"]);
+		}
+		[TestMethod]
+		public void Indexer_NullValueAddsJsonNull()
+		{
+			var obj = new JsonObject();
+			obj["null"] = null;
+
+			Assert.AreEqual(1, obj.Count);
+			Assert.AreEqual(JsonValue.Null, obj["null"]);
+		}
 	}
 }

Json.Tests/Json/Serialization/JsonSerializerTest.cs

 			Assert.AreEqual(expected, actual);
 		}
 		[TestMethod]
-		public void Deserialize_AbstractAndInterface_Successful()
+		public void Deserialize_AbstractAndInterfacePropsWithoutMap_Successful()
 		{
 			var json = new JsonObject{
 										{"AbstractProp", new JsonObject
 															{
 																{"#Type", typeof(DerivedClass).AssemblyQualifiedName},
-																{"#Value", new JsonObject{{"SomeProp", 42}}}
+																{"SomeProp", 42}
 															}},
 										{"InterfaceProp", new JsonObject
 															{
-																{"#Type", typeof(string).AssemblyQualifiedName},
-																{"#Value", "test comparable"}
+			                     								{"#Type", typeof (ImplementationClass).AssemblyQualifiedName},
+			                     								{"RequiredProp", "test"}
 															}}
 									};
 			var expected = new ObjectWithAbstractAndInterfaceProps
-							{
-								AbstractProp = new DerivedClass {SomeProp = 42},
-								InterfaceProp = "test comparable"
-							};
+			               	{
+			               		AbstractProp = new DerivedClass {SomeProp = 42},
+			               		InterfaceProp = new ImplementationClass {RequiredProp = "test"}
+			               	};
 			var actual = _serializer.Deserialize<ObjectWithAbstractAndInterfaceProps>(json);
 			Assert.AreEqual(expected, actual);
 		}
 		[TestMethod]
+		public void Deserialize_AbstractAndInterfacePropsWithMap_Successful()
+		{
+			var json = new JsonObject{
+										{"AbstractProp", new JsonObject
+															{
+																{"#Type", typeof(DerivedClass).AssemblyQualifiedName},
+																{"SomeProp", 42}
+															}},
+										{"InterfaceProp", new JsonObject {{"RequiredProp", "test"}}}
+									};
+			var expected = new ObjectWithAbstractAndInterfaceProps
+			               	{
+			               		AbstractProp = new DerivedClass {SomeProp = 42},
+			               		InterfaceProp = new ImplementationClass {RequiredProp = "test"}
+			               	};
+			JsonSerializationAbstractionMap.Map<Interface, ImplementationClass>();
+			var actual = _serializer.Deserialize<ObjectWithAbstractAndInterfaceProps>(json);
+			JsonSerializationAbstractionMap.RemoveMap<Interface>();
+			Assert.AreEqual(expected, actual);
+		}
+		[TestMethod]
+		public void Deserialize_AbstractClass_Successful()
+		{
+			var json = new JsonObject
+			           	{
+			           		{"#Type", typeof (DerivedClass).AssemblyQualifiedName},
+			           		{"SomeProp", 42},
+			           		{"NewProp", "test"}
+			           	};
+			AbstractClass expected = new DerivedClass
+			                         	{
+			                         		SomeProp = 42,
+			                         		NewProp = "test"
+			                         	};
+
+			var actual = _serializer.Deserialize<AbstractClass>(json);
+			Assert.AreEqual(expected, actual);
+		}
+		[TestMethod]
+		public void Deserialize_Interface_Successful()
+		{
+			JsonValue json = new JsonObject
+			                     	{
+			                     		{"#Type", typeof (ImplementationClass).AssemblyQualifiedName},
+			                     		{"RequiredProp", "test"}
+			                     	};
+			Interface expected = new ImplementationClass {RequiredProp = "test"};
+
+			var actual = _serializer.Deserialize<Interface>(json);
+			Assert.AreEqual(expected, actual);
+		}
+		[TestMethod]
 		public void Deserialize_Nullable_Null_Successful()
 		{
 			JsonSerializationTypeRegistry.RegisterNullableType<int>();
 			Assert.AreEqual(expected, actual);
 		}
 		[TestMethod]
-		public void Serialize_AbstractAndInterface_Successful()
+		public void Serialize_AbstractAndInterfaceProps_Successful()
 		{
 			var obj = new ObjectWithAbstractAndInterfaceProps
-						{
-							AbstractProp = new DerivedClass {SomeProp = 42},
-							InterfaceProp = "test comparable"
-						};
+			          	{
+			          		AbstractProp = new DerivedClass {SomeProp = 42},
+			          		InterfaceProp = new ImplementationClass {RequiredProp = "test comparable"}
+			          	};
 			JsonValue expected = new JsonObject
 									{
 										{"AbstractProp", new JsonObject
 															{
 																{"#Type", typeof(DerivedClass).AssemblyQualifiedName},
-																{"#Value", new JsonObject{{"SomeProp", 42}}}
-															}},
+																{"SomeProp", 42}}
+															},
 										{"InterfaceProp", new JsonObject
 															{
-																{"#Type", typeof(string).AssemblyQualifiedName},
-																{"#Value", "test comparable"}
-															}}
+																{"#Type", typeof(ImplementationClass).AssemblyQualifiedName},
+																{"RequiredProp", "test comparable"}}
+															}
 									};
 			var actual = _serializer.Serialize(obj);
 			Assert.AreEqual(expected, actual);
 		}
 		[TestMethod]
+		public void Serialize_AbstractClass_Successful()
+		{
+			AbstractClass obj = new DerivedClass
+			                    	{
+			                    		SomeProp = 42,
+										NewProp = "test"
+			                    	};
+			JsonValue expected = new JsonObject
+			                     	{
+			                     		{"#Type", typeof (DerivedClass).AssemblyQualifiedName},
+			                     		{"SomeProp", 42},
+			                     		{"NewProp", "test"}
+			                     	};
+
+			var actual = _serializer.Serialize(obj);
+			Assert.AreEqual(expected, actual);
+		}
+		[TestMethod]
+		public void Serialize_Interface_Successful()
+		{
+			Interface obj = new ImplementationClass {RequiredProp = "test"};
+			JsonValue expected = new JsonObject
+			                     	{
+			                     		{"#Type", typeof (ImplementationClass).AssemblyQualifiedName},
+			                     		{"RequiredProp", "test"}
+			                     	};
+
+			var actual = _serializer.Serialize(obj);
+			Assert.AreEqual(expected, actual);
+		}
+		[TestMethod]
 		public void Serialize_Nullable_Null_Successful()
 		{
 			JsonSerializationTypeRegistry.RegisterNullableType<int>();

Json.Tests/Manatee.Tests.csproj

     </CodeAnalysisDependentAssemblyPaths>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Json\Extensions\JsonExtensionsTest.cs" />
     <Compile Include="Json\Extensions\XmlExtensionsTest.cs" />
     <Compile Include="Json\JsonObjectTest.cs" />
     <Compile Include="Json\JsonArrayTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Test References\AbstractClass.cs" />
     <Compile Include="Test References\DerivedClass.cs" />
+    <Compile Include="Test References\ImplementationClass.cs" />
+    <Compile Include="Test References\Interface.cs" />
     <Compile Include="Test References\JsonCompatibleClass.cs" />
     <Compile Include="Test References\ObjectWithAbstractAndInterfaceProps.cs" />
     <Compile Include="Test References\ObjectWithBasicProps.cs" />

Json.Tests/Test References/DerivedClass.cs

 {
 	public class DerivedClass : AbstractClass
 	{
+		public string NewProp { get; set; }
+
+		public override bool Equals(object obj)
+		{
+			if (!(obj is DerivedClass)) return false;
+			return Equals((DerivedClass) obj);
+		}
+		public bool Equals(DerivedClass other)
+		{
+			if (ReferenceEquals(null, other)) return false;
+			if (ReferenceEquals(this, other)) return true;
+			return base.Equals(other) && Equals(other.NewProp, NewProp);
+		}
+		public override int GetHashCode()
+		{
+			unchecked
+			{
+				return (base.GetHashCode()*397) ^ (NewProp != null ? NewProp.GetHashCode() : 0);
+			}
+		}
 	}
 }

Json.Tests/Test References/ImplementationClass.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Manatee.Tests.Test_References
+{
+	public class ImplementationClass : Interface
+	{
+		public string RequiredProp { get; set; }
+
+		public override bool Equals(object obj)
+		{
+			if (!(obj is ImplementationClass)) return false;
+			return Equals((ImplementationClass) obj);
+		}
+		public bool Equals(ImplementationClass other)
+		{
+			if (ReferenceEquals(null, other)) return false;
+			if (ReferenceEquals(this, other)) return true;
+			return Equals(other.RequiredProp, RequiredProp);
+		}
+		public override int GetHashCode()
+		{
+			return (RequiredProp != null ? RequiredProp.GetHashCode() : 0);
+		}
+	}
+}

Json.Tests/Test References/Interface.cs

+namespace Manatee.Tests.Test_References
+{
+	public interface Interface
+	{
+		string RequiredProp { get; set; }
+	}
+}

Json.Tests/Test References/ObjectWithAbstractAndInterfaceProps.cs

 {
 	public class ObjectWithAbstractAndInterfaceProps
 	{
-		public IComparable InterfaceProp { get; set; }
+		public Interface InterfaceProp { get; set; }
 		public AbstractClass AbstractProp { get; set; }
 
 		public override bool Equals(object obj)

Binary file modified.

Json/Exceptions/JsonTypeMapException.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Manatee.Json.Exceptions
+{
+	public class JsonTypeMapException<TAbstract, TConcrete> : Exception
+	{
+		public JsonTypeMapException()
+			: base(string.Format("Cannot create map from type '{0}' to type '{1}' because the destination type is either abstract or an interface.",
+								 typeof(TAbstract),
+								 typeof(TConcrete))) {}
+	}
+}

Json/Extensions/JsonExtensions.cs

 		/// <returns>A bool or null if the key is not found or is not a string</returns>
 		public static bool? TryGetBoolean(this JsonObject obj, string key)
 		{
-			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type != JsonValueType.Null) ? obj[key].Boolean : (bool?)null;
+			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type == JsonValueType.Boolean) ? obj[key].Boolean : (bool?)null;
 		}
 		/// <summary>
 		/// Returns a JsonArray or null if the key is not found or is not a string.
 		/// <returns>A JsonArray or null if the key is not found or is not a string</returns>
 		public static JsonArray TryGetArray(this JsonObject obj, string key)
 		{
-			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type != JsonValueType.Null) ? obj[key].Array : null;
+			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type == JsonValueType.Array) ? obj[key].Array : null;
 		}
 		/// <summary>
 		/// Returns a JsonObject or null if the key is not found or is not a string.
 		/// <returns>A JsonObject or null if the key is not found or is not a string</returns>
 		public static JsonObject TryGetObject(this JsonObject obj, string key)
 		{
-			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type != JsonValueType.Null) ? obj[key].Object : null;
+			return (obj == null) ? null : obj.ContainsKey(key) && (obj[key].Type == JsonValueType.Object) ? obj[key].Object : null;
 		}
 	}
 }

Json/JsonArray.cs

 		{
 			var json = obj as JsonArray;
 			if (json == null) return false;
-			return ((from item in this where !json.Contains(item) select item).Count() == 0) &&
-					(from item in json where !Contains(item) select item).Count() == 0;
+			return this.All(json.Contains) && (Count == json.Count);
 		}
 
 		/// <summary>

Json/JsonObject.cs

 
 		static StateMachine<State, JsonInput> StateMachine = new StateMachine<State, JsonInput>();
 
-		string source, key;
-		int index;
-		JsonValue value;
-		bool done;
-		private InputStream<JsonInput> stream;
+		string _source, _key;
+		int _index;
+		JsonValue _value;
+		bool _done;
+		private InputStream<JsonInput> _stream;
+
+		/// <summary>
+		/// Gets or sets the value associated with the specified key.
+		/// </summary>
+		/// <param name="key">The key of the value to get or set.</param>
+		/// <returns>The value associated with the specified key.</returns>
+		public new JsonValue this[string key]
+		{
+			get { return base[key]; }
+			set { base[key] = value ?? JsonValue.Null; }
+		}
 
 		static JsonObject()
 		{
 			StateMachine[State.End, JsonInput.CloseBrace] = GotEnd;
 			StateMachine.UpdateFunction = GetNextInput;
 		}
-
 		/// <summary>
 		/// Creates an empty instance of a JSON object.
 		/// </summary>
 		public JsonObject() {}
-
 		/// <summary>
 		/// Creates an instance of a JSON object and fills it by parsing the
 		/// supplied string.
 		public JsonObject(string s)
 			: this()
 		{
-			source = StripExternalSpaces(s);
+			_source = StripExternalSpaces(s);
 			Parse(0);
 		}
-
 		internal JsonObject(string s, ref int i)
 			: this()
 		{
-			source = s;
+			_source = s;
 			i = Parse(i);
 		}
-
 		/// <summary>
 		/// Finalizes memory management responsibilities.
 		/// </summary>
 			s += string.Format("{0}\"{1}\" :\n{2}{3}\n{4}}}", tab1, key, tab2, this[key].GetIndentedString(indentLevel + 2), tab0);
 			return s;
 		}
+		/// <summary>
+		/// Adds the specified key and value to the dictionary.
+		/// </summary>
+		/// <param name="key">The key of the element to add.</param>
+		/// <param name="value">The value of the element to add. The value can be null for reference types.</param>
+		public new void Add(string key, JsonValue value)
+		{
+			base.Add(key, value ?? JsonValue.Null);
+		}
 
 		private static bool IsWhiteSpace(char c)
 		{
 		}
 		private int Parse(int i)
 		{
-			if (stream == null)
-				stream = new InputStream<JsonInput>();
+			if (_stream == null)
+				_stream = new InputStream<JsonInput>();
 			else
-				stream.Clear();
-			value = null;
-			index = i;
-			done = false;
+				_stream.Clear();
+			_value = null;
+			_index = i;
+			_done = false;
 			try
 			{
-				StateMachine.Run(this, State.Start, stream);
-				if (!done)
-					throw new JsonSyntaxException(index);
+				StateMachine.Run(this, State.Start, _stream);
+				if (!_done)
+					throw new JsonSyntaxException(_index);
 			}
 			catch (InputNotValidForStateException<State, JsonInput>)
 			{
-				throw new JsonSyntaxException(index);
+				throw new JsonSyntaxException(_index);
 			}
 			catch (StateNotValidException<State>)
 			{
-				throw new JsonSyntaxException(index);
+				throw new JsonSyntaxException(_index);
 			}
 			catch (ActionNotDefinedForStateAndInputException<State, JsonInput>)
 			{
-				throw new JsonSyntaxException(index);
+				throw new JsonSyntaxException(_index);
 			}
-			return index;
+			return _index;
 		}
 
 		private static string GetKey(string source, ref int index)
 		{
 			if (Count == 0) return "{}";
 			return "{" + string.Join(",", from kvp in this
-										  select string.Format("\"{0}\":{1}",kvp.Key,kvp.Value)) + "}";
+										  select string.Format("\"{0}\":{1}", kvp.Key, kvp.Value)) + "}";
 		}
 		/// <summary>
 		/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
 		{
 			var json = obj as JsonObject;
 			if (json == null) return false;
-			return ((from key in Keys where !json.ContainsKey(key) select key).Count() == 0) &&
-				   ((from key in json.Keys where !ContainsKey(key) select key).Count() == 0) &&
-				   this.All(pair => json[pair.Key].Equals(pair.Value));
+			return Keys.All(json.ContainsKey) && (Keys.Count == json.Keys.Count) &&
+			       this.All(pair => json[pair.Key].Equals(pair.Value));
 		}
 		/// <summary>
 		/// Serves as a hash function for a particular type. 
 		{
 			var obj = owner as JsonObject;
 			if (obj == null) return;
-			if (obj.done || (obj.index == obj.source.Length)) return;
+			if (obj._done || (obj._index == obj._source.Length)) return;
 			try
 			{
-				var next = CharacterConverter.Item(obj.source[obj.index++]);
-				obj.stream.Add(next);
+				var next = CharacterConverter.Item(obj._source[obj._index++]);
+				obj._stream.Add(next);
 			}
 			catch (KeyNotFoundException)
 			{
-				throw new JsonSyntaxException(obj.index);
+				throw new JsonSyntaxException(obj._index);
 			}
 		}
 		private static State GotStart(object owner, JsonInput input)
 		private static State GotKey(object owner, JsonInput input)
 		{
 			var obj = owner as JsonObject;
-			obj.key = GetKey(obj.source, ref obj.index);
+			obj._key = GetKey(obj._source, ref obj._index);
 			return State.Colon;
 		}
 		private static State GotColon(object owner, JsonInput input)
 		private static State GotValue(object owner, JsonInput input)
 		{
 			var obj = owner as JsonObject;
-			obj.value = JsonValue.Parse(obj.source, ref obj.index);
+			obj._value = JsonValue.Parse(obj._source, ref obj._index);
 			return State.End;
 		}
 		private static State GotEmpty(object owner, JsonInput input)
 		{
 			var obj = owner as JsonObject;
-			obj.done = (input == JsonInput.CloseBrace);
+			obj._done = (input == JsonInput.CloseBrace);
 			if (obj.Count != 0)
-				throw new JsonSyntaxException(obj.index);
+				throw new JsonSyntaxException(obj._index);
 			return State.Value;
 		}
 		private static State GotEnd(object owner, JsonInput input)
 		{
 			var obj = owner as JsonObject;
-			obj[obj.key] = obj.value;
-			obj.done = (input == JsonInput.CloseBrace);
+			obj[obj._key] = obj._value;
+			obj._done = (input == JsonInput.CloseBrace);
 			return State.Key;
 		}
 

Json/JsonValue.cs

 		///			{"stringData2", "another string"},
 		///			{"moreBoolData", false}}}};
 		/// </code></example>
-		public static implicit operator JsonValue(bool b)
+		public static implicit operator JsonValue(bool? b)
 		{
 			return new JsonValue(b);
 		}
 		///			{"stringData2", "another string"},
 		///			{"moreBoolData", false}}}};
 		/// </code></example>
-		public static implicit operator JsonValue(double n)
+		public static implicit operator JsonValue(double? n)
 		{
 			return new JsonValue(n);
 		}

Json/Manatee.Json.csproj

   </ItemGroup>
   <ItemGroup>
     <Compile Include="Attributes\JsonMapToAttribute.cs" />
+    <Compile Include="Exceptions\JsonTypeMapException.cs" />
     <Compile Include="Helpers\IObjectCaster.cs" />
     <Compile Include="Helpers\IPrimitiveMapper.cs" />
     <Compile Include="Helpers\ISerializerCache.cs" />
     <Compile Include="Helpers\ObjectCaster.cs" />
     <Compile Include="Helpers\PrimitiveMapper.cs" />
     <Compile Include="Serialization\IJsonCompatible.cs" />
+    <Compile Include="Serialization\JsonSerializationAbstractionMap.cs" />
     <Compile Include="Serialization\JsonSerializationTypeRegistry.cs" />
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\JsonSerializerOptions.cs" />

Json/Serialization/JsonSerializationAbstractionMap.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Manatee.Json.Enumerations;
+using Manatee.Json.Exceptions;
+
+namespace Manatee.Json.Serialization
+{
+	/// <summary>
+	/// Provides an interface to map abstract and interface types to
+	/// concrete types for object instantiation during deserialization.
+	/// </summary>
+	public static class JsonSerializationAbstractionMap
+	{
+		private static readonly Dictionary<Type, Type> _registry = new Dictionary<Type, Type>();
+
+		/// <summary>
+		/// Applies a mapping from an abstraction to a concrete type.
+		/// </summary>
+		/// <typeparam name="TAbstract">The abstract type.</typeparam>
+		/// <typeparam name="TConcrete">The concrete type.</typeparam>
+		public static void Map<TAbstract, TConcrete>()
+			where TConcrete : TAbstract, new()
+		{
+			if (typeof(TConcrete).IsAbstract || typeof(TConcrete).IsInterface)
+				throw new JsonTypeMapException<TAbstract, TConcrete>();
+			_registry[typeof (TAbstract)] = typeof (TConcrete);
+		}
+		/// <summary>
+		/// Removes a previously-assigned mapping.
+		/// </summary>
+		/// <typeparam name="TAbstract">The type to remove.</typeparam>
+		public static void RemoveMap<TAbstract>()
+		{
+			_registry.Remove(typeof (TAbstract));
+		}
+
+		internal static T CreateInstance<T>(JsonValue json)
+		{
+			var type = typeof (T);
+			if (type.IsAbstract || type.IsInterface)
+			{
+				if ((json.Type == JsonValueType.Object) && (json.Object.ContainsKey(JsonSerializer.TypeKey)))
+				{
+					var concrete = Type.GetType(json.Object[JsonSerializer.TypeKey].String);
+					return (T) Activator.CreateInstance(concrete);
+				}
+				if (_registry.ContainsKey(type))
+				{
+					var concrete = _registry[type];
+					return (T) Activator.CreateInstance(concrete);
+				}
+			}
+			return Activator.CreateInstance<T>();
+		}
+	}
+}

Json/Serialization/JsonSerializer.cs

 	/// </summary>
 	public class JsonSerializer
 	{
-		private const string TypeKey = "#Type";
-		private const string ValueKey = "#Value";
-		private const string RefKey = "#Ref";
-		private const string DefKey = "#Define";
+		internal const string TypeKey = "#Type";
+		internal const string ValueKey = "#Value";
+		internal const string RefKey = "#Ref";
+		internal const string DefKey = "#Define";
 
 		private static readonly ISerializerCache SerializerCache = new SerializerCache();
 		private static readonly IPrimitiveMapper PrimitiveMapper = new PrimitiveMapper(new ObjectCaster());
 			T obj;
 			if (typeof(IJsonCompatible).IsAssignableFrom(typeof(T)))
 			{
-				obj = Activator.CreateInstance<T>();
+				obj = JsonSerializationAbstractionMap.CreateInstance<T>(json);
 				((IJsonCompatible)obj).FromJson(json);
 			}
 			else if (json == JsonValue.Null)
 		private JsonValue AutoSerializeObject<T>(T obj)
 		{
 			var json = new JsonObject();
-			var propertyInfoList = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
-											.Where(p => p.GetSetMethod() != null)
-											.Where(p => p.GetGetMethod() != null)
-											.Where(p => !p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any());
+			var type = typeof (T);
+			if (type.IsAbstract || type.IsInterface)
+			{
+				type = obj.GetType();
+				json.Add(TypeKey, type.AssemblyQualifiedName);
+			}
+			var propertyInfoList = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
+									   .Where(p => p.GetSetMethod() != null)
+									   .Where(p => p.GetGetMethod() != null)
+									   .Where(p => !p.GetCustomAttributes(typeof (JsonIgnoreAttribute), true).Any());
 			foreach (var propertyInfo in propertyInfoList)
 			{
 				var value = propertyInfo.GetValue(obj, null);
 				if (value == null) continue;
-				var type = propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface
-					        ? value.GetType()
-					        : propertyInfo.PropertyType;
-				var serialize = SerializerCache.GetSerializer(type);
+				var propType = propertyInfo.PropertyType;
+				var serialize = SerializerCache.GetSerializer(propType);
 				var jsonProp = (JsonValue) serialize.Invoke(this, new[] {value});
 				if ((jsonProp == JsonValue.Null) && !Options.EncodeDefaultValues) continue;
 				var mapper = (JsonMapToAttribute)propertyInfo.GetCustomAttributes(typeof(JsonMapToAttribute), false).FirstOrDefault();
 				string name = mapper != null ? mapper.MapToKey : propertyInfo.Name;
-				json.Add(name,
-				         type == propertyInfo.PropertyType
-				         	? jsonProp
-				         	: new JsonObject {{TypeKey, type.AssemblyQualifiedName}, {ValueKey, jsonProp}});
+				json.Add(name, jsonProp);
 			}
 			return json.Count == 0 ? JsonValue.Null : json;
 		}
 		private T AutoDeserializeObject<T>(JsonValue json)
 		{
-			var propertyInfoList = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
-											.Where(p => p.GetSetMethod() != null)
-											.Where(p => p.GetGetMethod() != null)
-											.Where(p => !p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any());
-			var obj = Activator.CreateInstance<T>();
+			var obj = JsonSerializationAbstractionMap.CreateInstance<T>(json);
+			var type = obj.GetType();
+			var propertyInfoList = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
+									   .Where(p => p.GetSetMethod() != null)
+									   .Where(p => p.GetGetMethod() != null)
+									   .Where(p => !p.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Any());
 			foreach (var propertyInfo in propertyInfoList)
 			{
 				string name = propertyInfo.Name;
 				if (json.Object.ContainsKey(name))
 				{
 					var value = json.Object[name];
-					if ((value.Type == JsonValueType.Object) && (value.Object.ContainsKey(TypeKey)))
-					{
-						var instanceType = Type.GetType(value.Object[TypeKey].String);
-						var instanceJson = value.Object[ValueKey];
-						var deserialize = SerializerCache.GetDeserializer(instanceType);
-						propertyInfo.SetValue(obj, deserialize.Invoke(this, new object[] {instanceJson}), null);
-					}
-					else if ((value.Type == JsonValueType.Object) && (value.Object.ContainsKey(RefKey)))
+					if ((value.Type == JsonValueType.Object) && (value.Object.ContainsKey(RefKey)))
 					{
 						_referenceCache.AddReference(value.Object[RefKey].String, obj, propertyInfo);
 					}
 				}
 			}
 			if ((json.Object.Count > 0) && (Options.InvalidPropertyKeyBehavior == InvalidPropertyKeyBehavior.ThrowException))
-				throw new TypeDoesNotContainPropertyException(typeof (T), json);
+				throw new TypeDoesNotContainPropertyException(type, json);
 			return obj;
 		}
 		#endregion
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.