diff --git a/Assets/Plugins/UnityJSON/Attributes.cs b/Assets/Plugins/UnityJSON/Attributes.cs
index 8add72b..cefc2bc 100644
--- a/Assets/Plugins/UnityJSON/Attributes.cs
+++ b/Assets/Plugins/UnityJSON/Attributes.cs
@@ -10,7 +10,10 @@ namespace UnityJSON
/// attribute, the default options are used. For private fields and
/// properties, this attribute is mandatory.
///
- [AttributeUsage (AttributeTargets.Field | AttributeTargets.Property)]
+ [AttributeUsage (
+ AttributeTargets.Field |
+ AttributeTargets.Property |
+ AttributeTargets.Parameter)]
public class JSONNodeAttribute : Attribute
{
private NodeOptions _options;
@@ -320,4 +323,17 @@ public Type referenceType {
get { return _reference; }
}
}
+
+ ///
+ /// Marks a constructor that is used to deserialize objects.
+ /// Every class can use this attribute maximum one times. The
+ /// parameters can have JSONNodeAttributes.
+ ///
+ [AttributeUsage (AttributeTargets.Constructor)]
+ public class JSONConstructorAttribute : Attribute
+ {
+ public JSONConstructorAttribute () : base ()
+ {
+ }
+ }
}
diff --git a/Assets/Plugins/UnityJSON/Deserializer.cs b/Assets/Plugins/UnityJSON/Deserializer.cs
index 31888af..9ddb1b5 100644
--- a/Assets/Plugins/UnityJSON/Deserializer.cs
+++ b/Assets/Plugins/UnityJSON/Deserializer.cs
@@ -177,7 +177,7 @@ public Nullable DeserializeToNullable (
if (node == null || node.IsNull) {
return null;
}
- return (Nullable) Deserialize (node, typeof(T), options);
+ return (Nullable)Deserialize (node, typeof(T), options);
}
///
@@ -842,11 +842,16 @@ private object _DeserializeCustom (JSONNode node, Type type, NodeOptions options
var conditionalAttributes = type.GetCustomAttributes (typeof(ConditionalInstantiationAttribute), false);
foreach (object attribute in conditionalAttributes) {
var condition = attribute as ConditionalInstantiationAttribute;
- if (Equals (node [condition.key].Value, condition.value.ToString())) {
+ if (Equals (node [condition.key].Value, condition.value.ToString ())) {
+ JSONNode value = node [condition.key];
if (condition.removeKey) {
node.Remove (condition.key);
}
- return Deserialize (node, condition.referenceType, options);
+ object result = Deserialize (node, condition.referenceType, options);
+ if (condition.removeKey) {
+ node.Add (condition.key, value);
+ }
+ return result;
}
}
@@ -855,17 +860,62 @@ private object _DeserializeCustom (JSONNode node, Type type, NodeOptions options
return Deserialize (node, defaultAttribute.referenceType, options);
}
- object obj;
- try {
- obj = Activator.CreateInstance (type);
- } catch (Exception) {
- return _HandleUnknown (options, "Unknown type " + type + " cannot be instantiated.");
+ object obj = null;
+ KeyValuePair[] removedKeys = null;
+ ConstructorInfo[] constructors = type.GetConstructors (
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.NonPublic);
+ foreach (ConstructorInfo constructor in constructors) {
+ var constructorAttribute = Util.GetAttribute (constructor);
+ if (constructorAttribute != null) {
+ obj = _CreateObjectWithConstructor (type, constructor, node, out removedKeys);
+ break;
+ }
+ }
+
+ if (obj == null) {
+ try {
+ obj = Activator.CreateInstance (type);
+ } catch (Exception) {
+ return _HandleUnknown (options, "Unknown type " + type + " cannot be instantiated.");
+ }
}
DeserializeOn (obj, node, options);
+ if (removedKeys != null) {
+ foreach (var pair in removedKeys) {
+ node.Add (pair.Key, pair.Value);
+ }
+ }
return obj;
}
+ private object _CreateObjectWithConstructor (
+ Type type,
+ ConstructorInfo constructor,
+ JSONNode node,
+ out KeyValuePair[] removedKeys)
+ {
+ ParameterInfo[] parameters = constructor.GetParameters ();
+ object[] parameterValues = new object[parameters.Length];
+ removedKeys = new KeyValuePair[parameterValues.Length];
+
+ for (int i = 0; i < parameterValues.Length; i++) {
+ var parameterAttribute = Util.GetAttribute (parameters [i]);
+ string key = parameterAttribute != null && parameterAttribute.key != null
+ ? parameterAttribute.key : parameters [i].Name;
+ parameterValues [i] = Deserialize (
+ node [key],
+ parameters [i].ParameterType,
+ parameterAttribute == null ? NodeOptions.Default : parameterAttribute.options);
+
+ removedKeys [i] = new KeyValuePair (key, node [key]);
+ node.Remove (key);
+ }
+ return Activator.CreateInstance (type, parameterValues);
+ }
+
private void _FeedCustom (object filledObject, JSONNode node, NodeOptions options)
{
if (filledObject is IDeserializable) {
diff --git a/Assets/Plugins/UnityJSON/Interfaces.cs b/Assets/Plugins/UnityJSON/Interfaces.cs
index 9c7c9e9..e7f5aae 100644
--- a/Assets/Plugins/UnityJSON/Interfaces.cs
+++ b/Assets/Plugins/UnityJSON/Interfaces.cs
@@ -40,7 +40,7 @@ public interface IDeserializable
/// Feeds the JSON node into the object. You can use the
/// helper methods from the deserializer.
///
- void Deserialize(JSONNode node, Deserializer deserializer);
+ void Deserialize (JSONNode node, Deserializer deserializer);
}
///
@@ -53,19 +53,19 @@ public interface ISerializationListener
/// This call will always be followed by either OnSerializationSucceeded
/// or OnSerializationFailed but not both.
///
- void OnSerializationWillBegin(Serializer serializer);
+ void OnSerializationWillBegin (Serializer serializer);
///
/// Called immediately after a successful completion of this
/// object's serialization.
///
- void OnSerializationSucceeded(Serializer serializer);
+ void OnSerializationSucceeded (Serializer serializer);
///
/// Called when the serialization of the object fails for any
/// reason. This will be called just before throwing the exception.
///
- void OnSerializationFailed(Serializer serializer);
+ void OnSerializationFailed (Serializer serializer);
}
///
@@ -78,18 +78,18 @@ public interface IDeserializationListener
/// This call will always be followed by either OnDeserializationSucceeded
/// or OnDeserializationFailed but not both.
///
- void OnDeserializationWillBegin(Deserializer deserializer);
+ void OnDeserializationWillBegin (Deserializer deserializer);
///
/// Called immediately after a successful completion of this
/// object's deserialization.
///
- void OnDeserializationSucceeded(Deserializer deserializer);
+ void OnDeserializationSucceeded (Deserializer deserializer);
///
/// Called when the deserialization of the object fails for any
/// reason. This will be called just before throwing the exception.
///
- void OnDeserializationFailed(Deserializer deserializer);
+ void OnDeserializationFailed (Deserializer deserializer);
}
}
diff --git a/Assets/Plugins/UnityJSON/Serializer.cs b/Assets/Plugins/UnityJSON/Serializer.cs
index 5a92777..73b4303 100644
--- a/Assets/Plugins/UnityJSON/Serializer.cs
+++ b/Assets/Plugins/UnityJSON/Serializer.cs
@@ -49,7 +49,7 @@ public static Serializer Simple {
///
public bool useUndefinedForNull = false;
- private Serializer()
+ private Serializer ()
{
}
@@ -98,7 +98,7 @@ public string Serialize (object obj, NodeOptions options = NodeOptions.Default)
Type type = obj.GetType ();
if (type.IsValueType) {
if (type.IsEnum) {
- result = _SerializeEnum ((Enum) obj);
+ result = _SerializeEnum ((Enum)obj);
} else if (type.IsPrimitive) {
if (obj is bool) {
result = SerializeBool ((bool)obj);
@@ -236,7 +236,7 @@ public string SerializeVector3 (Vector3 vector)
public string SerializeVector4 (Vector4 vector)
{
return "{\"x\":" + vector.x + ",\"y\":" + vector.y
- + ",\"z\":" + vector.z + ",\"w\":" + vector.w + "}";
+ + ",\"z\":" + vector.z + ",\"w\":" + vector.w + "}";
}
///
@@ -245,7 +245,7 @@ public string SerializeVector4 (Vector4 vector)
public string SerializeQuaternion (Quaternion quaternion)
{
return "{\"x\":" + quaternion.x + ",\"y\":" + quaternion.y
- + ",\"z\":" + quaternion.z + ",\"w\":" + quaternion.w + "}";
+ + ",\"z\":" + quaternion.z + ",\"w\":" + quaternion.w + "}";
}
///
@@ -254,7 +254,7 @@ public string SerializeQuaternion (Quaternion quaternion)
public string SerializeColor (Color color)
{
return "{\"r\":" + color.r + ",\"g\":" + color.g
- + ",\"b\":" + color.b + ",\"a\":" + color.a + "}";
+ + ",\"b\":" + color.b + ",\"a\":" + color.a + "}";
}
///
@@ -263,7 +263,7 @@ public string SerializeColor (Color color)
private string SerializeRect (Rect rect)
{
return "{\"x\":" + rect.x + ",\"y\":" + rect.y
- + ",\"width\":" + rect.width + ",\"height\":" + rect.height + "}";
+ + ",\"width\":" + rect.width + ",\"height\":" + rect.height + "}";
}
///
@@ -272,7 +272,7 @@ private string SerializeRect (Rect rect)
private string SerializeBounds (Bounds bounds)
{
return "{\"center\":" + SerializeVector3 (bounds.center)
- + ",\"extents\":" + SerializeVector3 (bounds.extents) + "}";
+ + ",\"extents\":" + SerializeVector3 (bounds.extents) + "}";
}
private string _SerializeString (string stringValue)
@@ -286,7 +286,7 @@ private string _SerializeEnum (Enum obj)
JSONEnumAttribute enumAttribute = Util.GetAttribute (type);
if (enumAttribute != null) {
if (enumAttribute.useIntegers) {
- return (Convert.ToInt32(obj)).ToString ();
+ return (Convert.ToInt32 (obj)).ToString ();
} else {
string formatted = _FormatEnumMember (obj.ToString (), enumAttribute.format);
if (enumAttribute.prefix != null) {
@@ -395,14 +395,15 @@ where isNotExtras (p) && _IsValidPropertyInfo (p)
var extras = Util.GetMemberValue (extrasMember, obj) as IEnumerable;
if (extras != null) {
result += (result == "" ? "" : ",")
- + _SerializeEnumarable (extras, extrasAttribute.options);
+ + _SerializeEnumarable (extras, extrasAttribute.options);
}
}
if (listener != null) {
listener.OnSerializationSucceeded (this);
}
- return "{" + result + "}";;
+ return "{" + result + "}";
+ ;
} catch (Exception exception) {
if (listener != null) {
listener.OnSerializationFailed (this);
diff --git a/Assets/Plugins/UnityJSON/Util.cs b/Assets/Plugins/UnityJSON/Util.cs
index 30df74e..f388e13 100644
--- a/Assets/Plugins/UnityJSON/Util.cs
+++ b/Assets/Plugins/UnityJSON/Util.cs
@@ -13,6 +13,12 @@ internal static T GetAttribute (MemberInfo info) where T : Attribute
return (attributes == null || attributes.Length == 0) ? null : attributes [0] as T;
}
+ internal static T GetAttribute (ParameterInfo info) where T : Attribute
+ {
+ object[] attributes = info.GetCustomAttributes (typeof(T), true);
+ return (attributes == null || attributes.Length == 0) ? null : attributes [0] as T;
+ }
+
internal static Type GetMemberType (MemberInfo memberInfo)
{
return memberInfo is FieldInfo ?
diff --git a/README.md b/README.md
index 5b082c6..e1fe681 100644
--- a/README.md
+++ b/README.md
@@ -14,10 +14,13 @@
* [Deserialization](#deserialization)
- [Deserialized Types](#deserialized-types)
- [Deserialization of Extra Nodes](#deserialization-of-extra-nodes)
- - [Deserialization with Inheritance](#deserialization-with-inheritance)
+ - [Inheritance](#inheritance)
+ - [Constructors](#constructors)
- [Deserialization Lifecycle](#deserialization-lifecycle)
- [Custom Deserialization with Deserializer](#custom-deserialization-with-deserializer)
- [Custom Deserialization with IDeserializable](#custom-deserialization-with-ideserializable)
+* [Changelog](#changelog)
+ - [v1.1](#v11)
## Features
@@ -445,7 +448,7 @@ You can also use `RestrictTypeAttribute` to restrict the supported types or use
types. The extras are also used for the serialization and are serialized on the same level
as the object.
-### Deserialization with Inheritance
+### Inheritance
You may want to provide deserialization to interface or abstract class targets. One option
would be to use a custom deserializer
@@ -484,6 +487,26 @@ class C : I
I obj = JSON.Deserialize("{\"type\":1}"); // obj is of type B.
```
+### Constructors
+
+You can deserialize objects with custom constructors using the `JSONConstructor`
+attribute. The constructor arguments can have additional `JSONNode` attributes. Be
+aware that only one constructor can have this attribute.
+
+```cs
+class AClass
+{
+ private T _arg;
+
+ [JSONConstructor]
+ public AClass([JSONNode(key = "field")] T argument)
+ {
+ _arg = argument;
+ }
+}
+var obj = Deserialize>("{\"field\":1}");
+```
+
### Deserialization Lifecycle
You can listen to the deserialization lifecycle of an object by implementing the
@@ -596,3 +619,10 @@ public class AClass : IDeserializable
The classes that are deserialized with the `IDeserializable.Deserialize` method do
not receive deserialization lifecycle calls from `IDeserializationListener`.
+
+## Changelog
+
+### v1.1
+
+- Added `JSONConstructorAttribute`
+- Fixed conditional instantiation bug: JSONNode kept the same after key removal
diff --git a/unityjson.unitypackage b/unityjson.unitypackage
index 2ae240b..5cbc581 100644
Binary files a/unityjson.unitypackage and b/unityjson.unitypackage differ