且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Json.Net返回空括号

更新时间:2023-02-18 16:11:50

您的问题不包括您的Node<T>类的声明.在其他(大型)类中,我认为它一定是这样的:

Your question does not include the declaration of your Node<T> class. From the rest of the (large) class, I reckon it must be something like:

public class Node<T> : IEqualityComparer, IEnumerable<T>

这说明了您的问题:

  1. 您的课程为某些T实现IEnumerable<T>.
  2. Json.NET 将任何IEnumerable序列化为JSON数组
  3. JSON数组(根据标准)不支持命名属性.
  4. 因此,您的Value属性未序列化.
  1. Your class implements IEnumerable<T> for some T.
  2. Json.NET serializes any IEnumerable as a JSON array.
  3. JSON arrays (according to the standard) do not support named properties.
  4. Thus your Value property is not serialized.

如果您希望继续使Node<T>工具实现IEnumerable,则需要创建一个自定义 JsonConverter :

If you wish to continue making your Node<T> implement IEnumerable, you need to create a custom JsonConverter for it:

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

public class NodeConverter : JsonConverter
{
    class NodeWrapper<T>
    {
        public T value { get; set; }
        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public IEnumerable<Node<T>> children { get; set; }
    }

    static Type GetNodeValueType(Type type)
    {
        return type.BaseTypesAndSelf().Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Node<>)).Select(t => t.GetGenericArguments()[0]).FirstOrDefault();
    }

    public override bool CanConvert(Type objectType)
    {
        return GetNodeValueType(objectType) != null;
    }

    object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var wrapper = serializer.Deserialize<NodeWrapper<T>>(reader);
        if (wrapper == null)
            return existingValue;
        var node = existingValue as Node<T> ?? new Node<T>(wrapper.value);
        node.Value = wrapper.value;
        if (wrapper.children != null)
            foreach (var child in wrapper.children)
                node.Add(child);
        return node;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var method = GetType().GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
        var genericMethod = method.MakeGenericMethod(new[] { GetNodeValueType(objectType) });
        return genericMethod.Invoke(this, new object[] { reader, objectType, existingValue, serializer });
    }

    void WriteJsonGeneric<T>(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var node = (Node<T>)value;
        serializer.Serialize(writer, new NodeWrapper<T> { value = node.Value, children = (node.Children.Any() ? node.Children : null)});
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var method = GetType().GetMethod("WriteJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
        var genericMethod = method.MakeGenericMethod(new[] { GetNodeValueType(value.GetType()) });
        genericMethod.Invoke(this, new object[] { writer, value, serializer });
    }
}

通过调用您的Add(Node<T> child)方法,转换器还可以确保设置了父级后向引用.

By calling your Add(Node<T> child) method, the converter also makes sure the parent back-references are set.

然后像这样使用它:

[JsonConverter(typeof(NodeConverter))]
public class Node<T> : IEqualityComparer, IEnumerable<T>
{