将List<KeyValuePair<string, string>>序列化为JSON

51

我对JSON非常陌生,请帮忙!

我正在尝试将List<KeyValuePair<string, string>>序列化为JSON。

目前:

Original Answer翻译成"最初的回答"

[{"Key":"MyKey 1","Value":"MyValue 1"},{"Key":"MyKey 2","Value":"MyValue 2"}]

最初的回答
期望结果:
[{"MyKey 1":"MyValue 1"},{"MyKey 2":"MyValue 2"}]

"最初的回答":
我参考了这个例子和这个例子
这是我的KeyValuePairJsonConverter:JsonConverter。
public class KeyValuePairJsonConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<KeyValuePair<object, object>> list = value as List<KeyValuePair<object, object>>;
        writer.WriteStartArray();
        foreach (var item in list)
        {
            writer.WriteStartObject();
            writer.WritePropertyName(item.Key.ToString());
            writer.WriteValue(item.Value.ToString());
            writer.WriteEndObject();
        }
        writer.WriteEndArray();
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<KeyValuePair<object, object>>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var target = Create(objectType, jsonObject);
        serializer.Populate(jsonObject.CreateReader(), target);
        return target;
    }

    private object Create(Type objectType, JObject jsonObject)
    {
        if (FieldExists("Key", jsonObject))
        {
            return jsonObject["Key"].ToString();
        }

        if (FieldExists("Value", jsonObject))
        {
            return jsonObject["Value"].ToString();
        }
        return null;
    }

    private bool FieldExists(string fieldName, JObject jsonObject)
    {
        return jsonObject[fieldName] != null;
    }
}

我会在 WebService 方法中这样调用它:

最初的回答:

List<KeyValuePair<string, string>> valuesList = new List<KeyValuePair<string, string>>();
Dictionary<string, string> valuesDict = SomeDictionaryMethod();

foreach(KeyValuePair<string, string> keyValue in valuesDict)
{
    valuesList.Add(keyValue);
}

JsonSerializerSettings jsonSettings = new JsonSerializerSettings { Converters = new [] {new KeyValuePairJsonConverter()} };
string valuesJson = JsonConvert.SerializeObject(valuesList, jsonSettings);

1
请尝试在CanConvert方法中将´objectType == typeof(List<KeyValuePair<object, object>>);´更改为´objectType == typeof(List<KeyValuePair<string, string>>);´。 - Ralf Bönning
4
为什么你在使用KeyValuePairs的列表而不是字典(Dictionary)? - Sergey Berezovskiy
1
@SergeyBerezovskiy 你说得对!我想我在搜索要使用什么时迷失了方向,最终把事情搞得太复杂了。谢谢! - maryhadalittlelamb
3个回答

67
您可以使用Newtonsoft和字典:
    var dict = new Dictionary<int, string>();
    dict.Add(1, "one");
    dict.Add(2, "two");

    var output = Newtonsoft.Json.JsonConvert.SerializeObject(dict);

输出结果为:

{"1":"one","2":"two"}

编辑

感谢@Sergey Berezovskiy提供的信息。

您目前正在使用Newtonsoft,所以只需将您的List<KeyValuePair<object, object>>更改为Dictionary<object,object>,并使用该软件包中的序列化和反序列化方法。


所以他只需要将 List<KeyValuePair<object, object>> 更改为 Dictionnary<object,object>。我会编辑我的答案。 - OrcusZ
10
这就是我在评论中问他的内容。将KeyValuePairs列表更改为字典并非总是可能的。例如,您可以使用KVP作为传递键值的替代方法,而不是创建类或Tuple。您还可以拥有许多具有相同键的键值对,而这在字典中是不可能的。 - Sergey Berezovskiy
在他的create方法中,如果键已经存在,他会更改它,所以我认为我们更需要一个字典,但是你完全正确,在某些情况下,这种类型的列表也可能很有用 :) - OrcusZ
1
@OrcusZ 谢谢你的帮助!我将 List<KeyValuePair<string, string>> 更改为简单的 Dictionary<string, string>,序列化就可以正常工作了。 - maryhadalittlelamb
当你知道它时,它就变得很容易了。谢谢。本答案会从解释为什么的角度受益匪浅... 如何通过不同的序列化行为来解释KeyValuePair与Dictionary之间的区别? - Cee McSharpface
2
我不同意。List<KeyValuePair<object, object>> 保持插入顺序,而 Dictionary 则不保证。 - Kamaal

6

所以我不想使用除了本地的C#之外的任何东西来解决类似的问题,参考版本包括.net 4、jquery 3.2.1和backbone 1.2.0。

我的问题是,List<KeyValuePair<...>>会从控制器处理到backbone模型,但当我保存该模型时,控制器无法绑定List。

public class SomeModel {
    List<KeyValuePair<int, String>> SomeList { get; set; }
}

[HttpGet]
SomeControllerMethod() {
    SomeModel someModel = new SomeModel();
    someModel.SomeList = GetListSortedAlphabetically();
    return this.Json(someModel, JsonBehavior.AllowGet);
}

网络抓包:

"SomeList":[{"Key":13,"Value":"aaab"},{"Key":248,"Value":"aaac"}]

但是,即使在后台模型model.js中正确设置了SomeList,尝试保存模型而没有对其进行任何更改将导致绑定的SomeModel对象与请求正文中的参数具有相同的长度,但所有键和值都为null:

[HttpPut]
SomeControllerMethod([FromBody] SomeModel){
    SomeModel.SomeList; // Count = 2, all keys and values null.
}

我能找到的唯一信息是KeyValuePair是一个结构体,不能以这种方式实例化。我最终做的是以下操作:
  • Add a Model wrapper somewhere that contains key, value fields:

    public class KeyValuePairWrapper {
        public int Key { get; set; }
        public String Value { get; set; }
    
        //default constructor will be required for binding, the Web.MVC binder will invoke this and set the Key and Value accordingly.
        public KeyValuePairWrapper() { }
    
        //a convenience method which allows you to set the values while sorting
        public KeyValuePairWrapper(int key, String value)
        {
            Key = key;
            Value = value;
        }
    }
    
  • Set up your binding class model to accept your custom wrapper object.

    public class SomeModel
    {
        public List<KeyValuePairWrapper> KeyValuePairList{ get; set }; 
    }
    
  • Get some json data out of a controller

    [HttpGet]
    SomeControllerMethod() {
        SomeModel someModel = new SomeModel();
        someModel.KeyValuePairList = GetListSortedAlphabetically();
        return this.Json(someModel, JsonBehavior.AllowGet);
    }
    
  • Do something at a later time, maybe model.save(null, ...) is invoked

    [HttpPut]
    SomeControllerMethod([FromBody] SomeModel){
        SomeModel.KeyValuePairList ; // Count = 2, all keys and values are correct.
    }
    

3
之前的答案是正确的,但它们有些过于繁琐了。它们没有充分利用 Linq 的一行代码。
让我们回顾一下上下文:
你从这个 C# 类型开始:
List<KeyValuePair<string, string>>

...一旦序列化,就会变成这个Json:

[{"Key":"MyKey 1","Value":"MyValue 1"},{"Key":"MyKey 2","Value":"MyValue 2"}]

...但实际上你需要的是这个Json:

[{"MyKey 1":"MyValue 1"},{"MyKey 2":"MyValue 2"}]

...这将需要以下C#类型:

List<Dictionary<string, string>>

因此,您实际上想要将一个 C# 类型转换为另一个 C# 类型:

List<KeyValuePair<string, string>>   -->   List<Dictionary<string, string>>

注意:转换中唯一有点烦人的部分是这一部分:
KeyValuePair<string, string>  -->   Dictionary<string, string>

因为据我所知,没有直接的方法可以使用单个KeyValuePair创建仅包含它的Dictionary。您将在解决方案中看到,我首先将该单个KVP放入仅包含它的列表中,然后使用ToDictionary。

为了演示,我重新创建了您的C#输入:

var input = new List<KeyValuePair<string, string>>
{
            new KeyValuePair<string, string>("My Key 1", "MyValue1"),
            new KeyValuePair<string, string>("My Key2", "MyValue2")
};

然后你可以像这样进行转换(一行解决方案):

var converted = input
    .Select(singleKvp => (new List<KeyValuePair<string, string>>() { singleKvp }).ToDictionary(x => x.Key, x => x.Value))
    .ToList(); // Optional

您可以使用以下方式测试结果:

var output = JsonSerializer.Serialize(converted);

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