JSON.net: how to deserialize without using the default constructor?

后端 未结 5 1895
天涯浪人
天涯浪人 2020-11-22 07:21

I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are a

5条回答
  •  粉色の甜心
    2020-11-22 07:53

    A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.

    I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.

    I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.

    public class CustomContractResolver : DefaultContractResolver {
    
        protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            var c = base.CreateObjectContract(objectType);
            if (!IsCustomStruct(objectType)) return c;
    
            IList list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
            var mostSpecific = list.LastOrDefault();
            if (mostSpecific != null)
            {
                c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
                c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
            }
    
            return c;
        }
    
        protected virtual bool IsCustomStruct(Type objectType)
        {
            return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
        }
    
        private ObjectConstructor CreateParameterizedConstructor(MethodBase method)
        {
            method.ThrowIfNull("method");
            var c = method as ConstructorInfo;
            if (c != null)
                return a => c.Invoke(a);
            return a => method.Invoke(null, a);
        }
    }
    
    
    

    I'm using it like this.

    public struct Test {
      public readonly int A;
      public readonly string B;
    
      public Test(int a, string b) {
        A = a;
        B = b;
      }
    }
    
    var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
      ContractResolver = new CustomContractResolver()
    });
    var t = JsonConvert.DeserializeObject(json);
    t.A.ShouldEqual(1);
    t.B.ShouldEqual("Test");
    

    提交回复
    热议问题