【问题标题】:Deserializing JSON that has an int as a key in C#在 C# 中反序列化具有 int 作为键的 JSON
【发布时间】:2014-03-12 05:16:07
【问题描述】:

我正在尝试反序列化这个 JSON

{
"39": {
    "category": "Miscellaneous",
    "country_whitelist": [],
    "name": "domain.com",
    "url_blacklist": [],
    "country_blacklist": [],
    "url_whitelist": [
        "domain.com"
    ],
    "deals": {
        "425215": {
            "status": "Ok",
            "type": "",
            "code": "CODE",
            "end_date": "2014-03-01 04:00:00",
            "title": "RandomTitle",
            "url": "http://domain.com/foo",
            "text": "Text Text Text",
            "long_title": "Longer Text"
        },
        "425216": {
            "status": "Ok",
            "type": "",
            "code": "CODE2",
            "end_date": "2014-03-01 04:00:00",
            "title": "RandomTitle2",
            "url": "http://domain.com/bar",
            "text": "Text Text Text",
            "long_title": "Longer Text"
        }
    },
    "88x31": "http://someimage/88x31.png",
    "subcategory": "Other"
 },
"40": {
    "category": "Miscellaneous",
    "country_whitelist": [],
    "name": "domain.com",
    "url_blacklist": [],
    "country_blacklist": [],
    "url_whitelist": [
        "domain.com"
    ],
    "products": {
        "425215": {
            "status": "Ok",
            "type": "",
            "code": "CODE",
            "end_date": "2014-03-01 04:00:00",
            "title": "RandomTitle",
            "url": "http://domain.com/foo",
            "text": "Text Text Text",
            "long_title": "Longer Text"
        },
        "425216": {
            "status": "Ok",
            "type": "",
            "code": "CODE2",
            "end_date": "2014-03-01 04:00:00",
            "title": "RandomTitle2",
            "url": "http://domain.com/bar",
            "text": "Text Text Text",
            "long_title": "Longer Text"
        }
    },
    "88x31": "http://someimage/88x31.png",
    "subcategory": "Other"
 }
}

我尝试使用 Json.NET 并尝试使用 ServiceStack 的反序列化器,但我似乎无法获得此 JSON 的任何类型的表示。

我认为阻碍我的主要因素是密钥是 Int 但我无法控制收到的 JSON。

这是我构建的 C# 类

public class product
{
    public string status { get; set; }
    public string type { get; set; }
    public string code { get; set; }
    public string end_date { get; set; }
    public string title { get; set; }
    public string url { get; set; }
    public string text { get; set; }
    public string long_title { get; set; }
}

public class Merchant
{
    public string category { get; set; }
    public List<string> country_whitelist { get; set; }
    public string name { get; set; }
    public List<string> url_blacklist { get; set; }
    public List<string> country_blacklist { get; set; }
    public List<string> url_whitelist { get; set; }
    public List<product> products { get; set; }
    public string subcategory { get; set; }
}

public class Data
{
    public Dictionary<int, Merchant> MainMerchants { get; set; }
}

我更喜欢使用 ServiceStack,但任何其他可用的反序列化器都会很棒

var data = client.Get(json);

【问题讨论】:

  • 您的 json 字符串缺少结束结尾的 '}'

标签: c# json servicestack json-deserialization


【解决方案1】:

正确映射数据类型:

可以反序列化您的 JSON。正如您正确识别的那样,您可以反序列化为Dictionary&lt;int, Merchant&gt;

但您需要将Merchant 类中products 的定义更改为Dictionary&lt;int, Product&gt;。它需要是一个字典来处理你的数字键。 List&lt;Product&gt; 不起作用。

还要处理88x31 属性,您可以使用DataMember(Name = '88x31') 映射将其映射到c# 喜欢的东西,例如image88x31。不幸的是,这确实意味着您的 DTO 属性成为可选的,因此您需要装饰所有成员。 添加using System.Runtime.Serialization;

一旦您做出如下更改:

// Note I capitalized Product
public class Product
{
    public string status { get; set; }
    public string type { get; set; }
    public string code { get; set; }
    public string end_date { get; set; }
    public string title { get; set; }
    public string url { get; set; }
    public string text { get; set; }
    public string long_title { get; set; }
}

/*
 * Use DataMember to map the keys starting with numbers to an alternative c# compatible name.
 * Unfortunately this requires properties to opt in to the data contract.
 */
[DataContract]
public class Merchant
{
    [DataMember]
    public string category { get; set; }

    [DataMember]
    public List<string> country_whitelist { get; set; }

    [DataMember]
    public string name { get; set; }

    [DataMember]
    public List<string> url_blacklist { get; set; }

    [DataMember]
    public List<string> country_blacklist { get; set; }

    [DataMember]
    public List<string> url_whitelist { get; set; }

    [DataMember]
    public Dictionary<int, Product>  products { get; set; }

    [DataMember]
    public string sub_category { get; set; }

    // This maps the 88x31 key to a c# appropriate name
    [DataMember(Name = "88x31")]
    public string image88x31 { get; set; }
}

然后您将能够反序列化为Dictionary&lt;int, Merchant&gt; 而不会出现任何问题。

JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>("YOUR JSON STRING");

在 ServiceStack 服务中使用:

如果您希望能够将此请求直接发送到 ServiceStack 服务,那么您可以使用 RequestBinder 反序列化为这种复杂类型。鉴于这项服务:

请求 DTO:

[Route("/Merchants", "POST")]
public class MerchantsRequest
{
    public Dictionary<int, Merchant> MainMerchants { get; set; }
}

简单的操作方法:

public class MerchantsService : Service
{
    public void Post(MerchantsRequest request)
    {
        var merchant39 = request.MainMerchants.First(p=>p.Key == 39).Value;
        Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant39.name, merchant39.image88x31, merchant39.products.Count);

        var merchant40 = request.MainMerchants.First(p=>p.Key == 40).Value;
        Console.WriteLine("Name: {0}\nImage: {1}\nProduct Count: {2}", merchant40.name, merchant40.image88x31, merchant40.products.Count);
    }
}

AppHost 配置:

在您的AppHost Configure 方法中,您需要向请求类型添加一个活页夹。即typeof(MerchantsRequest) 就像这样:

public override void Configure(Funq.Container container)
{
    Func<IRequest, object> merchantsRequestBinder = delegate(IRequest request) {
        var json = WebUtility.HtmlDecode( request.GetRawBody() );
        return new MerchantsRequest { MainMerchants = JsonSerializer.DeserializeFromString<Dictionary<int, Merchant>>(json) };
    };

    RequestBinders.Add(typeof(MerchantsRequest), merchantsRequestBinder);

    ...
}

此 binder 方法会将您发送的 json 转换为 MerchantsRequest。然后你可以像普通的 ServiceStack 请求一样使用它。

Full Source Code Here

一个完整的控制台应用程序示例,展示了复杂 JSON 到服务请求的转换。


注意: 我注意到在您的 JSON 中,您在一个对象上具有属性 deals,在另一个对象上具有 products,我认为这是一个错字,因为您没有对应的deals 的类中的属性。

【讨论】:

  • 是的,正是我想说的,JSON 字符串与给定的类型不兼容
【解决方案2】:

在您的 json 字符串中,对于 products 节点,应该是这个吗?因为它转换的类型是列表而不是字典?

我可以让它工作,将其更改为以下 json 字符串

"products": [{

        "status": "Ok",
        "type": "",
        "code": "CODE",
        "end_date": "2014-03-01 04:00:00",
        "title": "RandomTitle",
        "url": "http://domain.com/foo",
        "text": "Text Text Text",
        "long_title": "Longer Text"
    },
    {
        "status": "Ok",
        "type": "",
        "code": "CODE2",
        "end_date": "2014-03-01 04:00:00",
        "title": "RandomTitle2",
        "url": "http://domain.com/bar",
        "text": "Text Text Text",
        "long_title": "Longer Text"

}],

【讨论】:

  • 我知道它会这样工作,但我写的 JSON 是我拥有的,我无法更改它。
  • 那么,您是说您为“产品”提供的 json 节点是从 Dictionary 序列化的,而您想反序列化为 List
  • 不...我正在尝试将其反序列化为一个类。关键是从给定的 JSON 中获得一个硬类型的 c# 类
猜你喜欢
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-19
  • 1970-01-01
  • 2017-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多