JSON.NET自定义构造函数

时间:2013-12-07 12:06:32

标签: c# constructor json.net deserialization converter

作为此问题的部分问题JSON.NET CustomCreationConverter with nested objects我试图在反序列化期间调用自定义构造函数。我简化的类层次结构如下:

public abstract class BusinessObjectBase
{
    internal BusinessObjectBase(SerializationContext context)
        : base(context)
    {
    }
}

public abstract class EditableObjectBase : BusinessObjectBase
{
    protected EditableObjectBase(SerializationContext context)
        : base(context)
    {
    }
}

public class EditableObjectCollection<TObject> : BusinessObjectBase, ICollection<TObject>, IList, INotifyCollectionChanged where TObject : BusinessObjectBase
{
    protected EditableObjectCollection(SerializationContext context)
        : base(context)
    {
    }
}

我知道某个级别的对象层次结构,但允许/强制用户派生自己的类。我的想法是编写自定义创建转换器。我正在解决的问题是序列化对象中的属性可以声明为抽象的BusinessObjectBase,但真实对象将是一个更加派生的类,可能是一个集合或不是。 CustomCreationConverter只获取传递给Create方法的抽象类型,当然也无法从此信息中创建正确的类型。

受此启发How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects我实现了如下转换器:

internal class BusinessObjectCreationConverter : JsonConverter
{
    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(BusinessObjectBase).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object result = null;

        if (reader.TokenType != JsonToken.Null)
        {
            JObject jsonObject = JObject.Load(reader);
            result = this.Create(objectType, jsonObject);
            Verification.Assert<NullReferenceException>(result != null, "No Business Object created.");
            serializer.Populate(jsonObject.CreateReader(), result);
        }

        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public BusinessObjectBase Create(Type objectType, JObject jsonObject)
    {
        JToken token       = jsonObject.SelectToken("$type");
        var typeString     = token.Value<string>();
        Type type          = Type.GetType(typeString);
        var businessObject = type.CreateUsingDesrializationConstructor<BusinessObjectBase>();

        businessObject.Initialize(true);
        return businessObject;
    }
}

我测试序列化的类看起来像这样:

public class AnyPocoContainingBusinessObject
{
    public BusinessObjectBase BusinessObject { get; set; }    
}

public class TestEditableObject : EditableObjectBase
{
    internal TestEditableObject(SerializationContext context)
        : base(context)
    {
    }
 }

如果我使用集合

初始化我的班级
var collection = new EditableObjectCollection<TestEditableObject>(null);
var poco = new AnyPocoContainingBusinessObject { BusinessObject = collection };

并以这种方式配置序列化程序:

    public NewtonsoftJsonSerializer()
        : this(new JsonSerializer
        {
            TypeNameHandling = TypeNameHandling.Auto,
            ObjectCreationHandling = ObjectCreationHandling.Replace,
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
            DefaultValueHandling = DefaultValueHandling.Ignore,
            ContractResolver = new KsJsonContractResolver()
        })
    {
        this.serializer.Converters.Add(new ReadOnlyObjectCollectionConverter());
        this.serializer.Converters.Add(new BusinessObjectCreationConverter());
        this.serializer.TraceWriter = new ConsoleTraceWriter();
    }

我得到一个例外: 无法将JSON对象填充到类型'KS.Interfaces.Core.Entities.EditableObjectCollection`1 [KS.Interfaces.Core.Entities.Tests.Unit.EditableObjectCollectionTests + TestEditableObject]'上。路径'$ type',第1行,第47位。

在我的转换器的代码行中:

serializer.Populate(jsonObject.CreateReader(), result);

任何人都能告诉我可能是什么原因吗?我很确定我创建了正确的类型,并且使用EditableObjectBase派生对象一切都很好。只有集合似乎不起作用。

任何提示都非常感谢,提前谢谢 卡斯滕

1 个答案:

答案 0 :(得分:1)

即使我没有找到一种方法让我的转换器工作,我在调试问题时学到了一件事:

转换器似乎应返回仍具有相同JsonToken值的对象。在我的例子中,原始对象的JsonToken是JsonToken.Object,但是对于我的转换结果,正确的标记值将是JsonToken.Array,但读者仍然看到JsonToken.Object。此时我停止了研究,因为我发现了一种更好的方法来调用我的自定义构造函数。

我写了自己的合同解析员:

   internal class BusinessBaseContractResolver : DefaultContractResolver
   {
        public BusinessBaseContractResolver()
        {
            this.DefaultMembersSearchFlags |= BindingFlags.NonPublic;    
        }

        public override JsonContract ResolveContract(Type type)
        {
            JsonContract contract = base.ResolveContract(type);
            if (typeof(BusinessObjectBase).IsAssignableFrom(type))
            {
                contract.DefaultCreator = delegate
                {
                    var businessObject = type.CreateUsingDeserializationConstructor<BusinessObjectBase>();
                    businessObject.Initialize(true);

                    return businessObject;
                };
            }

            return contract;
        }
    }

我希望这有助于某人。

祝你好运, 卡斯滕