Skip to content

Commit

Permalink
feat: support tuple format in structs and classes
Browse files Browse the repository at this point in the history
  • Loading branch information
adragonite committed Sep 27, 2017
1 parent 96b7751 commit 30637da
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 82 deletions.
17 changes: 13 additions & 4 deletions Assets/Plugins/UnityJSON/Deserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ public Instantiater instantiater {
}
}

protected Deserializer ()
{
}

/// <summary>
/// Tries to deserialize the JSON node onto the given object. It is guaranteed
/// that the object is not null. This will be called before trying any other
Expand Down Expand Up @@ -1360,12 +1364,17 @@ private Dictionary<string, List<MemberInfo>> _GetDeserializedClassMembers (
out MemberInfo extrasMember,
out JSONExtrasAttribute extrasAttribute)
{
JSONObjectAttribute classAttribute = Util.GetAttribute<JSONObjectAttribute> (classType);
JSONObjectAttribute objectAttribute = Util.GetAttribute<JSONObjectAttribute> (classType);
Dictionary<string, List<MemberInfo>> members = new Dictionary<string, List<MemberInfo>> ();

var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
if (classAttribute != null && !classAttribute.options.ShouldIgnoreStatic ()) {
flags |= BindingFlags.Static;
if (objectAttribute != null) {
if (!objectAttribute.options.ShouldIgnoreStatic ()) {
flags |= BindingFlags.Static;
}
if (objectAttribute.options.ShouldUseTupleFormat ()) {
throw new ArgumentException ("Cannot deserialize on a tuple formatted object.");
}
}

extrasMember = null;
Expand All @@ -1392,7 +1401,7 @@ private Dictionary<string, List<MemberInfo>> _GetDeserializedClassMembers (
}
}

if (classAttribute == null || !classAttribute.options.ShouldIgnoreProperties ()) {
if (objectAttribute == null || !objectAttribute.options.ShouldIgnoreProperties ()) {
foreach (var propertyInfo in classType.GetProperties(flags)) {
if (extrasMember == null) {
if (Util.IsJSONExtrasMember (propertyInfo, out extrasAttribute)) {
Expand Down
45 changes: 33 additions & 12 deletions Assets/Plugins/UnityJSON/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,20 @@ public enum ObjectOptions
/// keys, then an exception is thrown. This option prevents the
/// deserializer from throwing that exception.
/// </summary>
IgnoreUnknownKey = 1 << 2
IgnoreUnknownKey = 1 << 2,

/// <summary>
/// The class or the struct is handled as a tuple (JSON array) rather
/// than a dictionary. This automatically ignores properties both for
/// serialization and deserialization. For serialization, the fields are
/// serialized in the order they are defined in an array without keys.
/// As for deserialization, the elements are passed to the constructor
/// in the order they are defined. Deserialization does not take place
/// for other fields and properties.
///
/// Tuple formatted classes and structs do not support JSON extras.
/// </summary>
TupleFormat = 1 << 3 | IgnoreProperties,
}

/// <summary>
Expand Down Expand Up @@ -225,23 +238,23 @@ public static bool IsDeserialized (this NodeOptions options)
/// </summary>
public static bool ShouldSerializeNull (this NodeOptions options)
{
return (options & NodeOptions.SerializeNull) != 0;
return (options & NodeOptions.SerializeNull) == NodeOptions.SerializeNull;
}

/// <summary>
/// Returns <c>true</c> if NodeOptions.IgnoreTypeMismatch is set.
/// </summary>
public static bool ShouldIgnoreTypeMismatch (this NodeOptions options)
{
return (options & NodeOptions.IgnoreTypeMismatch) != 0;
return (options & NodeOptions.IgnoreTypeMismatch) == NodeOptions.IgnoreTypeMismatch;
}

/// <summary>
/// Returns <c>true</c> if NodeOptions.IgnoreUnknownType is set.
/// </summary>
public static bool ShouldIgnoreUnknownType (this NodeOptions options)
{
return (options & NodeOptions.IgnoreInstantiationError) != 0;
return (options & NodeOptions.IgnoreInstantiationError) == NodeOptions.IgnoreInstantiationError;
}

/// <summary>
Expand All @@ -257,15 +270,15 @@ public static bool ShouldAssignNull (this NodeOptions options)
/// </summary>
public static bool ShouldReplaceWithDeserialized (this NodeOptions options)
{
return (options & NodeOptions.ReplaceDeserialized) != 0;
return (options & NodeOptions.ReplaceDeserialized) == NodeOptions.ReplaceDeserialized;
}

/// <summary>
/// Returns <c>true</c> if ObjectOptions.IgnoreProperties is set.
/// </summary>
public static bool ShouldIgnoreProperties (this ObjectOptions options)
{
return (options & ObjectOptions.IgnoreProperties) != 0;
return (options & ObjectOptions.IgnoreProperties) == ObjectOptions.IgnoreProperties;
}

/// <summary>
Expand All @@ -284,52 +297,60 @@ public static bool ShouldThrowAtUnknownKey (this ObjectOptions options)
return (options & ObjectOptions.IgnoreUnknownKey) == 0;
}

/// <summary>
/// Returns <c>true</c> if ObjectOptions.TupleFormat is not set.
/// </summary>
public static bool ShouldUseTupleFormat (this ObjectOptions options)
{
return (options & ObjectOptions.TupleFormat) == ObjectOptions.TupleFormat;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.String is set.
/// </summary>
public static bool SupportsString (this ObjectTypes types)
{
return (types & ObjectTypes.String) != 0;
return (types & ObjectTypes.String) == ObjectTypes.String;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.Bool is set.
/// </summary>
public static bool SupportsBool (this ObjectTypes types)
{
return (types & ObjectTypes.Bool) != 0;
return (types & ObjectTypes.Bool) == ObjectTypes.Bool;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.Number is set.
/// </summary>
public static bool SupportsNumber (this ObjectTypes types)
{
return (types & ObjectTypes.Number) != 0;
return (types & ObjectTypes.Number) == ObjectTypes.Number;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.Array is set.
/// </summary>
public static bool SupportsArray (this ObjectTypes types)
{
return (types & ObjectTypes.Array) != 0;
return (types & ObjectTypes.Array) == ObjectTypes.Array;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.Dictionary is set.
/// </summary>
public static bool SupportsDictionary (this ObjectTypes types)
{
return (types & ObjectTypes.Dictionary) != 0;
return (types & ObjectTypes.Dictionary) == ObjectTypes.Dictionary;
}

/// <summary>
/// Returns <c>true</c> if ObjectTypes.Custom is set.
/// </summary>
public static bool SupportsCustom (this ObjectTypes types)
{
return (types & ObjectTypes.Custom) != 0;
return (types & ObjectTypes.Custom) == ObjectTypes.Custom;
}
}
}
70 changes: 49 additions & 21 deletions Assets/Plugins/UnityJSON/Instantiater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public class Instantiater
{
public static readonly Instantiater Default = new Instantiater ();

protected Instantiater ()
{
}

/// <summary>
/// Instantiates an instance of a type. First, TryInstantiate method is
/// called for custom instantiation. If that fails, then the class is queried for
Expand Down Expand Up @@ -252,22 +256,28 @@ private InstantiationData _Instantiate (
return instantiationData;
}

if (node.IsNull || node.Tag == JSONNodeType.None) {
return InstantiationData.Null;
}

if (referingType != targetType) {
var conditionalAttributes = targetType
.GetCustomAttributes (typeof(ConditionalInstantiationAttribute), false);
foreach (object attribute in conditionalAttributes) {
var condition = attribute as ConditionalInstantiationAttribute;
if (Equals (node [condition.key].Value, condition.value.ToString ())) {
instantiationData = _Instantiate (
node,
condition.referenceType,
targetType,
options,
deserializer);
if (condition.ignoreConditionKey) {
instantiationData.ignoredKeys = new HashSet<string> () { condition.key };
if (node.IsObject) {
var conditionalAttributes = targetType
.GetCustomAttributes (typeof(ConditionalInstantiationAttribute), false);
foreach (object attribute in conditionalAttributes) {
var condition = attribute as ConditionalInstantiationAttribute;
if (Equals (node [condition.key].Value, condition.value.ToString ())) {
instantiationData = _Instantiate (
node,
condition.referenceType,
targetType,
options,
deserializer);
if (condition.ignoreConditionKey) {
instantiationData.ignoredKeys = new HashSet<string> () { condition.key };
}
return instantiationData;
}
return instantiationData;
}
}

Expand Down Expand Up @@ -295,19 +305,32 @@ private InstantiationData _InstantiateWithConstructor (
NodeOptions options,
Deserializer deserializer)
{
if (node.IsNull || node.Tag == JSONNodeType.None) {
return InstantiationData.Null;
}

JSONObjectAttribute objectAttribute = Util.GetAttribute<JSONObjectAttribute> (targetType);
bool useTupleFormat = objectAttribute != null
? objectAttribute.options.ShouldUseTupleFormat () : false;
if (useTupleFormat && !node.IsArray) {
throw new InstantiationException ("Expected JSON array, found " + node.Tag);
} else if (!useTupleFormat && !node.IsObject) {
throw new InstantiationException ("Expected JSON object, found " + node.Tag);
}

ConstructorInfo[] constructors = targetType.GetConstructors (
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
foreach (ConstructorInfo constructorInfo in constructors) {
var constructorAttribute = Util.GetAttribute<JSONConstructorAttribute> (constructorInfo);
if (constructorAttribute != null) {
return _InstantiateWithConstructor (node, constructorInfo, deserializer);
return _InstantiateWithConstructor (node, constructorInfo, deserializer, useTupleFormat);
}
}

try {
InstantiationData instantiationData = new InstantiationData();
InstantiationData instantiationData = new InstantiationData ();
instantiationData.instantiatedObject = Activator.CreateInstance (targetType);
instantiationData.needsDeserialization = node.Count != 0;
return instantiationData;
Expand All @@ -320,7 +343,8 @@ private InstantiationData _InstantiateWithConstructor (
private InstantiationData _InstantiateWithConstructor (
JSONNode node,
ConstructorInfo constructorInfo,
Deserializer deserializer)
Deserializer deserializer,
bool useTupleFormat)
{
ParameterInfo[] parameters = constructorInfo.GetParameters ();
object[] parameterValues = new object[parameters.Length];
Expand All @@ -332,23 +356,27 @@ private InstantiationData _InstantiateWithConstructor (

string key = nodeAttribute != null && nodeAttribute.key != null
? nodeAttribute.key : parameters [i].Name;
JSONNode parameterNode = useTupleFormat ? node [i] : node [key];

ObjectTypes restrictedTypes = restrictAttribute == null
? ObjectTypes.JSON : restrictAttribute.types;
Type[] customTypes = restrictAttribute == null ? null : restrictAttribute.customTypes;

parameterValues [i] = deserializer.Deserialize (
node [key],
parameterNode,
parameters [i].ParameterType,
nodeAttribute == null ? NodeOptions.Default : nodeAttribute.options,
restrictedTypes,
customTypes);

ignoredKeys.Add (key);
if (!useTupleFormat) {
ignoredKeys.Add (key);
}
}

InstantiationData instantiationData = new InstantiationData();
InstantiationData instantiationData = new InstantiationData ();
instantiationData.instantiatedObject = constructorInfo.Invoke (parameterValues);
instantiationData.needsDeserialization = ignoredKeys.Count != node.Count;
instantiationData.needsDeserialization = !useTupleFormat && ignoredKeys.Count != node.Count;
instantiationData.ignoredKeys = ignoredKeys;
return instantiationData;
}
Expand Down
Loading

0 comments on commit 30637da

Please sign in to comment.