自定义对象序列化与PreserveReferencesHandling

用户名

有什么标准的方法可以"$id"在序列化时获取当前对象的字段值,并"$id"在使用自定义时在反序列化时通过其获取对象JsonConverter

数据库

自定义JsonConverter内部,您可以使用return IReferenceResolverbyJsonSerializer.ReferenceResolver手动读取和写入Json.NET的"$id""$ref"属性。

以下转换器为此提供了一个模板:

public abstract class ReferenceHandlingCustomCreationConverter<T> : JsonConverter where T : class
{
    const string refProperty = "$ref";
    const string idProperty = "$id";

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

    protected virtual T Create(Type objectType, T existingValue, JsonSerializer serializer, JObject obj)
    {
        return existingValue ?? (T)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
    }

    protected abstract void Populate(JObject obj, T value, JsonSerializer serializer);

    protected abstract void WriteProperties(JsonWriter writer, T value, JsonSerializer serializer, JsonObjectContract contract);

    public override sealed object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(objectType);
        if (!(contract is JsonObjectContract))
        {
            throw new JsonSerializationException(string.Format("Invalid non-object contract type {0}", contract));
        }
        if (!(existingValue == null || existingValue is T))
        {
            throw new JsonSerializationException(string.Format("Converter cannot read JSON with the specified existing value. {0} is required.", typeof(T)));
        }

        if (reader.MoveToContent().TokenType == JsonToken.Null)
            return null;
        var obj = JObject.Load(reader);

        var refId = (string)obj[refProperty].RemoveFromLowestPossibleParent();
        var objId = (string)obj[idProperty].RemoveFromLowestPossibleParent();
        if (refId != null)
        {
            var reference = serializer.ReferenceResolver.ResolveReference(serializer, refId);
            if (reference != null)
                return reference;
        }

        var value = Create(objectType, (T)existingValue, serializer, obj);

        if (objId != null)
        {
            // Add the empty array into the reference table BEFORE poppulating it, to handle recursive references.
            serializer.ReferenceResolver.AddReference(serializer, objId, value);
        }

        Populate(obj, value, serializer);

        return value;
    }

    public override sealed void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(value.GetType());
        if (!(contract is JsonObjectContract))
        {
            throw new JsonSerializationException(string.Format("Invalid non-object contract type {0}", contract));
        }
        if (!(value is T))
        {
            throw new JsonSerializationException(string.Format("Converter cannot read JSON with the specified existing value. {0} is required.", typeof(T)));
        }

        writer.WriteStartObject();

        if (serializer.ReferenceResolver.IsReferenced(serializer, value))
        {
            writer.WritePropertyName(refProperty);
            writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value));
        }
        else
        {
            writer.WritePropertyName(idProperty);
            writer.WriteValue(serializer.ReferenceResolver.GetReference(serializer, value));

            WriteProperties(writer, (T)value, serializer, (JsonObjectContract)contract);
        }

        writer.WriteEndObject();
    }
}

public static partial class JsonExtensions
{
    public static JsonReader MoveToContent(this JsonReader reader)
    {
        if (reader.TokenType == JsonToken.None)
            reader.Read();
        while (reader.TokenType == JsonToken.Comment && reader.Read())
            ;
        return reader;
    }

    public static JToken RemoveFromLowestPossibleParent(this JToken node)
    {
        if (node == null)
            return null;
        var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
        if (contained != null)
            contained.Remove();
        // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
        if (node.Parent is JProperty)
            ((JProperty)node.Parent).Value = null;
        return node;
    }
}

实现转换器需要实现两种抽象方法:

protected abstract void Populate(JObject obj, T value, JsonSerializer serializer);

protected abstract void WriteProperties(JsonWriter writer, T value, JsonSerializer serializer, JsonObjectContract contract);

一种默认的通用实现可能类似于:

public class DefaultReferenceHandlingCustomCreationConverter<T> : ReferenceHandlingCustomCreationConverter<T> where T : class
{
    protected override void Populate(JObject obj, T value, JsonSerializer serializer)
    {
        using (var reader = obj.CreateReader())
            serializer.Populate(reader, value);
    }

    protected override void WriteProperties(JsonWriter writer, T value, JsonSerializer serializer, JsonObjectContract contract)
    {
        foreach (var property in contract.Properties.Where(p => p.Writable && !p.Ignored))
        {
            // TODO: handle JsonProperty attributes including
            // property.Converter, property.IsReference, property.ItemConverter, property.ItemReferenceLoopHandling, 
            // property.ItemReferenceLoopHandling, property.ObjectCreationHandling, property.ReferenceLoopHandling, property.Required                            
            var itemValue = property.ValueProvider.GetValue(value);
            writer.WritePropertyName(property.PropertyName);
            serializer.Serialize(writer, itemValue);
        }
    }
}

使用它,您将按以下步骤进行序列化:

var settings = new JsonSerializerSettings
{
    Converters = { new DefaultReferenceHandlingCustomCreationConverter<RootObject>() },
    ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
};
var json = JsonConvert.SerializeObject(parent, Formatting.Indented, settings);

笔记:

  • 该转换器旨在与序列化为JSON对象的c#类一起使用。这也将是可能创造一个转换器,手动写入和读取"$ref""$id"并且"$values"如在集合类,例如性能这个答案不能维持基准从非默认构造函数创建数组或只读列表,或列表

  • 该转换器将在反序列化过程中拆分创建和填充对象的任务,因此将无法与仅使用参数化构造函数的对象一起使用。这是使递归自引用正确解析所必需的。

  • ReferenceLoopHandling.Serialize需要序列化

样品在这里摆弄

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章