使用 JsonConvert.DeserializeObject 或 JObject.Parse 将类反序列化为 c# 中的字典

Posted

技术标签:

【中文标题】使用 JsonConvert.DeserializeObject 或 JObject.Parse 将类反序列化为 c# 中的字典【英文标题】:Deserialize a class to a dictionary in c# using JsonConvert.DeserializeObject or JObject.Parse 【发布时间】:2019-07-15 16:47:09 【问题描述】:

我正在 Asp.Net Core 2.2 中制作一个小型 API 项目,用于获取国家、货币、语言等数据(静态数据)。

假设我的 JSON 格式如下:


  "data": 
    "countries": [
      
        "translatedName": "Ascension Island",
        "translations": [
          
            "languageCode": "bg-BG",
            "translatedName": "Остров Възнесение"
          ,
          
            "languageCode": "cs-CZ",
            "translatedName": "Ascension"
          
        ]
   

我只想获取data 标签对象中的 JSON 文本并将其反序列化为此类:

class Country

      public string TranslatedName  get; set; 

      public Dictionary<string, string> Translations  get; set; 
 

所以这个想法是字典的键是languageCode,值是translatedName。

data 对象将出现在我收到的任何类型的响应中,而对于另一部分它可能会改变。可能是data -&gt; countries,或者data -&gt; languages,或者data -&gt; currencies等等。

所以解析的方法如下所示;

public T Parse<T>(string fieldName, string json)



fieldName 将确定data 键之后的第二个子名称,因此结构将始终为data -&gt; fieldName。我可以通过说string.Replace(...) 来替换 JSON 中未使用的(数据键)部分,但我正在考虑是否有更好的方法来做到这一点!

第二个问题是如何将翻译反序列化为字典?

我尝试在方法中做这样的事情:

public T Parse<T>(string fieldName, string json)

    return JObject.Parse(json)[fieldName].ToObject<T>();

所以如果我像这样使用这种方法:

var countries = Parse<List<Country>>("countries", ....); //it fails due to the fact it does not know how to convert translations to dictionary

我非常感谢任何帮助:

【问题讨论】:

对于第一个问题,我通常只使用一个包装器:public class Wrapper&lt;T&gt; public T Data get; set; 并将其用于反序列化 "字典的键是 languageCode,值是 translateName。" 不幸的是,没有从 json 直接映射到您尝试执行的操作。 Json 对象可以表示为字典,但是此类字典的键将是 json 属性名称。根据您的 json,这样的字典将具有例如字符串 "languageCode" 作为键,字符串值为 "bg-BG"。您需要处理“翻译”列表中的 json 对象并相应地构建您的字典... @CamiloTerevinto 是的,我其实也是这么想的,但后来我搞砸了第二部分,不知何故完全忘记了包装纸(手掌) 保持强类型并创建一个翻译类以便使用public List&lt;Translation&gt; Translations get; set; 会有什么问题吗? @CamiloTerevinto 的问题是,在第二步中我搜索翻译,如果它是一个列表,那么与字典相比它的性能不高。所以我想要一本字典的唯一原因是提高搜索性能。我可以再添加一个属性并根据列表创建该字典,但正在考虑是否有更好的方法,并且由于这是一个库,因此消费者会混淆使用哪个属性,因为其中一个属性应该被取消,因为它被缓存了之后。因此,如果消费者尝试使用 Translations 列表,则会得到一个空异常 【参考方案1】:

我在这里为你创建了这个函数,经过测试,它可以与你的 json 字符串一起使用。

var json = " data:  countries: [ translatedName: 'Ascension Island', translations: [ languageCode: 'bg-BG', translatedName: 'Остров Възнесение',  languageCode: 'cs-CZ',translatedName: 'Ascension' ]] ";
List<Country> countries = Parse<List<Country>>(json, "data.countries");
public static T Parse<T>(string json, string key)

    var rss = JObject.Parse(json);
    JToken jtoken = null;
    foreach (var k in key.Split('.'))
    
        if (jtoken != null)
            jtoken = jtoken[k];
        else jtoken = rss[k];

    

    return jtoken.ToObject<T>();

【讨论】:

【参考方案2】:

终于可以解决问题了:

对于@Camilo 建议的第一个问题,我创建了一个包装器。 对于第二个,我创建了一个自定义 JsonConverter,它为我完成了如下所示的工作。

internal class TranslationsToDictionaryObjectConverter : JsonConverter

    public override bool CanConvert(Type objectType)
    
        return typeof(List<CountryInfoModel>).IsAssignableFrom(objectType) 
            || typeof(List<CurrencyInfoModel>).IsAssignableFrom(objectType);
    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        JToken token = JToken.Load(reader);
        Dictionary<string, string> dict = new Dictionary<string, string>();
        try
        
            foreach (var item in token)
            
                dict.Add(item["languageCode"].ToString(), item["translatedName"].ToString());
            
        
        catch
        
            // ignored
        

        return dict;
    

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

之后模型将如下所示:

[Serializable]
public class CountryInfoModel 

    public int BwinId  get; set; 

    public string TranslatedName  get; set; 

    public string TwoLetterCode  get; set; 

    public bool LoginAllowed  get; set; 

    public bool RegistrationAllowed  get; set; 


    [JsonConverter(typeof(TranslationsToDictionaryObjectConverter))]
    public Dictionary<string, string> Translations  get; set; 

【讨论】:

你究竟为什么要这么做?我告诉过你,它适用于默认的序列化程序。你做错了什么 @CamiloTerevinto 这里是一个小提琴:dotnetfiddle.net/WQlfF7#,检查错误 因为结构错误,你有Dictionary&lt;string, string&gt; Translations,而你应该有List&lt;Dictionary&lt;string, string&gt;&gt; Translations @CamiloTerevinto 对我来说没有意义为每个翻译创建一个字典,最后翻译是一个键值对(twoLetterCode - translateName)而不是电子字典,你可以那样做好吧,但没有任何好处,尤其是在搜索方面,因为您仍然必须在列表中搜索,然后从仅包含条目的字典中获取数据

以上是关于使用 JsonConvert.DeserializeObject 或 JObject.Parse 将类反序列化为 c# 中的字典的主要内容,如果未能解决你的问题,请参考以下文章

测试使用

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”