如何将Lookup<string, string>对象序列化为JSON?

11

我正在尝试通过以下方式将Lookup序列化为JSON:

Lookup<string, string> productIdsByCategory = prodcuts.ToLookup(p => p.Category, p => p.Id);

然后在我的ASP.NET Core WebAPI控制器中,我通过以下方式返回:

var response = new ComplexType
{
     PropertyA = productIdsByCategory
     ... other properties left out ...
};
return Json(response)

我的WebAPI在Postman中的响应如下所示:

{
  "PropertyA": [
    [
      "ProductId_A"
    ],
    [
      "ProductId_B"
    ],
    [
      "ProductId_C"
    ]
  ],
  ... other properties left out ...
}

JSON响应中不包含密钥,这是否可能?

2个回答

11
作为创建自定义转换器的解决方法,您可以将Lookup<TKey, TElement>转换为Dictionary<TKey,List<TElement>>,这样Newtonsoft.Json可以正常工作。
myLookup.ToDictionary(x => x.Key, x => x.ToList());

2
这是一个非常好的解决方法。不幸的是,由于字典对象涉及哈希码计算,它并不那么高效。 - Larry

9

您是正确的,Newtonsoft.Json 对 Lookup<TKey, TElement> 处理得不好。下面是代码:

var input = new List<string>()
{
    "A1",
    "A2",
    "B1",
    "C1",
    "C2"
};

var lookup = input.ToLookup(i => i[0], i => i);
var json = JsonConvert.SerializeObject(lookup);

Console.WriteLine(json);        

序列化为:
[["A1","A2"],["B1"],["C1","C2"]]

您正确地指出了缺少关键名称(ABC),它似乎被序列化为一个数组的数组。
似乎没有内置的支持来序列化查找,因此您将不得不编写自己的转换器。
我在这里找到了一个转换器,但这需要您事先知道键和值的类型。
修改后,这就完成了工作:
        // Same as above, up till here
        var json = JsonConvert.SerializeObject(lookup, new LookupSerializer());
        Console.WriteLine(json);
    }
}

public class LookupSerializer : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        var result = objectType.GetInterfaces().Any(a => a.IsGenericType 
            && a.GetGenericTypeDefinition() == typeof(ILookup<,>));
        return result;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var obj = new JObject();            
        var enumerable = (IEnumerable)value;

        foreach (object kvp in enumerable)
        {
            // TODO: caching
            var keyProp = kvp.GetType().GetProperty("Key");
            var keyValue = keyProp.GetValue(kvp, null);

            obj.Add(keyValue.ToString(), JArray.FromObject((IEnumerable)kvp));
        }

        obj.WriteTo(writer);
    }
}

输出:

{
    "A": ["A1", "A2"],
    "B": ["B1"],
    "C": ["C1", "C2"]
}

很好的答案。如果有人像我一样使用自定义设置,并希望在序列化时使用它们,可以将serializer参数添加到对IEnumerable进行序列化的行中;obj.Add(keyValue.ToString(), JArray.FromObject((IEnumerable)kvp, **serializer**)); - DaggeJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接