反序列化具有未知字段的JSON

3
我正在尝试使用c#和json.net从OctoPart API(http://octopart.com/api/docs/v3/rest-api)反序列化结果。大部分数据的反序列化没有问题,但是我在处理Part.specs属性(http://octopart.com/api/docs/v3/rest-api#notes-part.specs)时遇到了麻烦,因为这些属性取决于返回的物品。

以下是API关于Part.Specs的说明:

Part实例附加的specs属性是将属性简称(例如“voltage_rating_dc”)映射到QualitativeValueQuantitativeValue实例的JSON对象。
需要注意的是,(Qual|Quant)itativeValue对象的所有值属性都是JSON数组。原因是为了适应具有多个输出电压的电源等多值属性:

因为我想保留这个类,所以我认为我可能需要实现一个自定义的JConverter? 我看了这个question,但不太确定如何应用到这个例子中,因为属性可以反序列化为QualitativeValue / QuantitativeValue类。

这是我的Part类

public class Part
{
    public string __class__ { get; set; }
    public string uid { get; set; }
    public long uid_v2 { get; set; }
    public string mpn { get; set; }
    public Manufacturer manufacturer { get; set; }
    public Brand brand { get; set; }
    public string octopart_url { get; set; }
    public List<PartOffer> offers { get; set; }
    public List<Datasheet> datasheets { get; set; }
    public List<ComplianceDocument> compliance_documents { get; set; }
    public List<Description> descriptions { get; set; }
    public List<ImageSet> imagesets { get; set; }
    public Dictionary<string, string> specs { get; set; }
    public List<string> category_uids { get; set; }
    public List<ExternalLinks> external_links { get; set; }
}

以下是来自API的示例结果(PartsMatchResponse)
{
    "__class__": "PartsMatchResponse",
    "msec": 183,
    "request": {
        "__class__": "PartsMatchRequest",
        "exact_only": false,
        "queries": [
            {
                "__class__": "PartsMatchQuery",
                "brand": null,
                "limit": 10,
                "mpn": "ERJ8BWFR010V",
                "mpn_or_sku": null,
                "q": "",
                "reference": null,
                "seller": null,
                "sku": null,
                "start": 0
            }
        ]
    },
    "results": [
        {
            "__class__": "PartsMatchResult",
            "error": null,
            "hits": 1,
            "items": [
                {
                    "__class__": "Part",
                    "brand": {
                        "__class__": "Brand",
                        "name": "Panasonic - ECG",
                        "uid": "4c528d5878c09b95"
                    },
                    "category_uids": [
                        "7542b8484461ae85",
                        "cd01000bfc2916c6",
                        "5c6a91606d4187ad"
                    ],
                    "compliance_documents": [],
                    "datasheets": null,
                    "external_links": {
                        "__class__": "ExternalLinks",
                        "evalkit_url": null,
                        "freesample_url": null,
                        "product_url": null
                    },
                    "imagesets": null,
                    "manufacturer": {
                        "__class__": "Manufacturer",
                        "name": "Panasonic - ECG",
                        "uid": "c20a0700af7c11cd"
                    },
                    "mpn": "ERJ8BWFR010V",
                    "octopart_url": "http://octopart.com/erj8bwfr010v-panasonic+-+ecg-7979066",
                    "offers": null,
                    "specs": {
                        "case_package": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": []
                            },
                            "value": [
                                "1206"
                            ]
                        },
                        "case_package_si": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": []
                            },
                            "value": [
                                "3216"
                            ]
                        },
                        "lead_free_status": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": [
                                    {
                                        "__class__": "Source",
                                        "name": "Future Electronics",
                                        "uid": "e4032109c4f337c4"
                                    }
                                ]
                            },
                            "value": [
                                "Lead Free"
                            ]
                        },
                        "lifecycle_status": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": []
                            },
                            "value": [
                                "Not Listed by Manufacturer"
                            ]
                        },
                        "pin_count": {
                            "__class__": "QuantitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": [
                                    {
                                        "__class__": "Source",
                                        "name": "Farnell",
                                        "uid": "58989d9272cd8b5f"
                                    }
                                ]
                            },
                            "max_value": null,
                            "min_value": null,
                            "unit": null,
                            "value": [
                                "2"
                            ]
                        },
                        "power_rating": {
                            "__class__": "QuantitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": [
                                    {
                                        "__class__": "Source",
                                        "name": "Newark",
                                        "uid": "d294179ef2900153"
                                    }
                                ]
                            },
                            "max_value": null,
                            "min_value": null,
                            "unit": null,
                            "value": [
                                "0.5"
                            ]
                        },
                        "resistance": {
                            "__class__": "QuantitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": [
                                    {
                                        "__class__": "Source",
                                        "name": "Farnell",
                                        "uid": "58989d9272cd8b5f"
                                    }
                                ]
                            },
                            "max_value": null,
                            "min_value": null,
                            "unit": null,
                            "value": [
                                "0.01"
                            ]
                        },
                        "resistance_tolerance": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": []
                            },
                            "value": [
                                "\u00b11%"
                            ]
                        },
                        "rohs_status": {
                            "__class__": "QualitativeValue",
                            "attribution": {
                                "__class__": "Attribution",
                                "first_acquired": null,
                                "sources": [
                                    {
                                        "__class__": "Source",
                                        "name": "Newark",
                                        "uid": "d294179ef2900153"
                                    }
                                ]
                            },
                            "value": [
                                "Compliant"
                            ]
                        }
                    },
                    "uid": "69e8a09b8cb4b62f",
                    "uid_v2": 797906654705
                }
            ],
            "reference": null
        }
    ]
}
2个回答

1

是的,您需要一个自定义的JsonConverter来解决这个问题。基本上您需要做的是:

  1. 定义你的QualitativeValueQuantitativeValue类有一个共同的基类(例如AbstractQValue)或接口。
  2. 在你的Parts类中,将specs属性设置为Dictionary<string, AbstractQValue>。 这将处理更改后的属性名称。
  3. 创建一个自定义的JsonConverter来处理根据JSON中的__class__属性实例化具体的QualitativeValueQuantitativeValue。 请参见this answer以了解如何实现此操作的示例。
  4. 最后,在反序列化时,请确保将自定义JsonConverter的实例传递到JsonConvert.DeserializeObject方法中。

演示

我有一点时间,所以我组织了一个可工作的示例。这里是数据的类定义(为简洁起见,我删去了大部分无关紧要的内容):

public class PartsMatchResponse
{
    public List<PartsMatchResult> results { get; set; }
}

public class PartsMatchResult
{
    public List<Part> items { get; set; }
}

public class Part
{
    public Manufacturer manufacturer { get; set; }
    public string mpn { get; set; }
    public Dictionary<string, AbstractQValue> specs { get; set; }
}

public class Manufacturer
{
    public string name { get; set; }
}

public abstract class AbstractQValue
{
    public List<string> value { get; set; }
}

public class QualitativeValue : AbstractQValue
{
}

public class QuantitativeValue : AbstractQValue
{
    public string unit { get; set; }
}

这是自定义的JsonConverter类:

public class QValueJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(AbstractQValue).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, 
        Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        if (jo["__class__"].ToString() == "QuantitativeValue")
        {
            return jo.ToObject<QuantitativeValue>();
        }
        return jo.ToObject<QualitativeValue>();
    }

    public override void WriteJson(JsonWriter writer, 
        object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

以下是关于如何在反序列化时使用转换器的示例演示程序:
class Program
{
    static void Main(string[] args)
    {
        // (jsonString is defined as a constant below)

        PartsMatchResponse response = 
            JsonConvert.DeserializeObject<PartsMatchResponse>(jsonString, 
                new QValueJsonConverter());

        foreach (Part part in response.results[0].items)
        {
            Console.WriteLine("manufacturer: " + part.manufacturer.name);
            Console.WriteLine("mfr. part no: " + part.mpn);
            foreach (KeyValuePair<string, AbstractQValue> kvp in part.specs)
            {
                string unit = "";
                if (kvp.Value is QuantitativeValue) 
                    unit = ((QuantitativeValue)kvp.Value).unit;

                Console.WriteLine(kvp.Key + ": " + 
                    string.Join(", ", kvp.Value.value) + " " + unit);
            }
            Console.WriteLine();
        }
    }

    // Note: this is the same as the example JSON in the question, except 
    // I added units for some of the QuantitativeValue specs for demo purposes.

    const string jsonString = @"
        {
            ""__class__"": ""PartsMatchResponse"",
            ""msec"": 183,
            ""request"": {
                ""__class__"": ""PartsMatchRequest"",
                ""exact_only"": false,
                ""queries"": [
                    {
                        ""__class__"": ""PartsMatchQuery"",
                        ""brand"": null,
                        ""limit"": 10,
                        ""mpn"": ""ERJ8BWFR010V"",
                        ""mpn_or_sku"": null,
                        ""q"": """",
                        ""reference"": null,
                        ""seller"": null,
                        ""sku"": null,
                        ""start"": 0
                    }
                ]
            },
            ""results"": [
                {
                    ""__class__"": ""PartsMatchResult"",
                    ""error"": null,
                    ""hits"": 1,
                    ""items"": [
                        {
                            ""__class__"": ""Part"",
                            ""brand"": {
                                ""__class__"": ""Brand"",
                                ""name"": ""Panasonic - ECG"",
                                ""uid"": ""4c528d5878c09b95""
                            },
                            ""category_uids"": [
                                ""7542b8484461ae85"",
                                ""cd01000bfc2916c6"",
                                ""5c6a91606d4187ad""
                            ],
                            ""compliance_documents"": [],
                            ""datasheets"": null,
                            ""external_links"": {
                                ""__class__"": ""ExternalLinks"",
                                ""evalkit_url"": null,
                                ""freesample_url"": null,
                                ""product_url"": null
                            },
                            ""imagesets"": null,
                            ""manufacturer"": {
                                ""__class__"": ""Manufacturer"",
                                ""name"": ""Panasonic - ECG"",
                                ""uid"": ""c20a0700af7c11cd""
                            },
                            ""mpn"": ""ERJ8BWFR010V"",
                            ""octopart_url"": ""http://octopart.com/erj8bwfr010v-panasonic+-+ecg-7979066"",
                            ""offers"": null,
                            ""specs"": {
                                ""case_package"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": []
                                    },
                                    ""value"": [
                                        ""1206""
                                    ]
                                },
                                ""case_package_si"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": []
                                    },
                                    ""value"": [
                                        ""3216""
                                    ]
                                },
                                ""lead_free_status"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": [
                                            {
                                                ""__class__"": ""Source"",
                                                ""name"": ""Future Electronics"",
                                                ""uid"": ""e4032109c4f337c4""
                                            }
                                        ]
                                    },
                                    ""value"": [
                                        ""Lead Free""
                                    ]
                                },
                                ""lifecycle_status"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": []
                                    },
                                    ""value"": [
                                        ""Not Listed by Manufacturer""
                                    ]
                                },
                                ""pin_count"": {
                                    ""__class__"": ""QuantitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": [
                                            {
                                                ""__class__"": ""Source"",
                                                ""name"": ""Farnell"",
                                                ""uid"": ""58989d9272cd8b5f""
                                            }
                                        ]
                                    },
                                    ""max_value"": null,
                                    ""min_value"": null,
                                    ""unit"": null,
                                    ""value"": [
                                        ""2""
                                    ]
                                },
                                ""power_rating"": {
                                    ""__class__"": ""QuantitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": [
                                            {
                                                ""__class__"": ""Source"",
                                                ""name"": ""Newark"",
                                                ""uid"": ""d294179ef2900153""
                                            }
                                        ]
                                    },
                                    ""max_value"": null,
                                    ""min_value"": null,
                                    ""unit"": ""Watt"",
                                    ""value"": [
                                        ""0.5""
                                    ]
                                },
                                ""resistance"": {
                                    ""__class__"": ""QuantitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": [
                                            {
                                                ""__class__"": ""Source"",
                                                ""name"": ""Farnell"",
                                                ""uid"": ""58989d9272cd8b5f""
                                            }
                                        ]
                                    },
                                    ""max_value"": null,
                                    ""min_value"": null,
                                    ""unit"": ""Ohm"",
                                    ""value"": [
                                        ""0.01""
                                    ]
                                },
                                ""resistance_tolerance"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": []
                                    },
                                    ""value"": [
                                        ""\u00b11%""
                                    ]
                                },
                                ""rohs_status"": {
                                    ""__class__"": ""QualitativeValue"",
                                    ""attribution"": {
                                        ""__class__"": ""Attribution"",
                                        ""first_acquired"": null,
                                        ""sources"": [
                                            {
                                                ""__class__"": ""Source"",
                                                ""name"": ""Newark"",
                                                ""uid"": ""d294179ef2900153""
                                            }
                                        ]
                                    },
                                    ""value"": [
                                        ""Compliant""
                                    ]
                                }
                            },
                            ""uid"": ""69e8a09b8cb4b62f"",
                            ""uid_v2"": 797906654705
                        }
                    ],
                    ""reference"": null
                }
            ]
        }";
}

最后,这是上面程序的输出结果:
manufacturer: Panasonic - ECG
mfr. part no: ERJ8BWFR010V
case_package: 1206
case_package_si: 3216
lead_free_status: Lead Free
lifecycle_status: Not Listed by Manufacturer
pin_count: 2
power_rating: 0.5 Watt
resistance: 0.01 Ohm
resistance_tolerance: ±1%
rohs_status: Compliant

太好了,非常感谢您的帮助。我会尽快尝试这个! - Spacko

0
您需要这些类来建模JSON。
public class OctopartObject
{
    public string __class__ { get; set; }
    public int msec { get; set; }
    public Request request { get; set; }
    public List<Result> results { get; set; }
}

public class Query
{
    public string __class__ { get; set; }
    public object brand { get; set; }
    public int limit { get; set; }
    public string mpn { get; set; }
    public object mpn_or_sku { get; set; }
    public string q { get; set; }
    public object reference { get; set; }
    public object seller { get; set; }
    public object sku { get; set; }
    public int start { get; set; }
}

public class Request
{
    public string __class__ { get; set; }
    public bool exact_only { get; set; }
    public List<Query> queries { get; set; }
}

public class Brand
{
    public string __class__ { get; set; }
    public string name { get; set; }
    public string uid { get; set; }
}

public class Manufacturer
{
    public string __class__ { get; set; }
    public string name { get; set; }
    public string uid { get; set; }
}

public class Item
{
    public string __class__ { get; set; }
    public Brand brand { get; set; }
    public Manufacturer manufacturer { get; set; }
    public string mpn { get; set; }
    public string octopart_url { get; set; }
    public List<object> offers { get; set; }
    public string uid { get; set; }
    public object uid_v2 { get; set; }
}

public class Result
{
    public string __class__ { get; set; }
    public object error { get; set; }
    public int hits { get; set; }
    public List<Item> items { get; set; }
    public object reference { get; set; }
}

然后使用JSON.NET和.NET 4.5,可以像这样做。

HttpClient client = new HttpClient();

// Send a request asynchronously and continue when complete
HttpResponseMessage clientResult = await client.GetAsync(_address);

// Check that response was successful or throw exception
clientResult.EnsureSuccessStatusCode();

// Read response asynchronously as JToken and write out top facts for each country
string jsonString = await clientResult.Content.ReadAsStringAsync();

OctopartObject obj = JsonConvert.DeserializeObject<OctopartObject>(jsonString);

你将拥有一个很好的对象,应该模拟从_address URI接收到的数据

我还没有完全测试过这个,所以可能会有一些问题。但是我已经在这上面挣扎了几个小时,最终找到了一些看起来可以工作的东西。我确定它不适用于数据表和任何额外的字段,因为它只返回基本信息。但是我基本上只是使用了这个网站来获取对象模型,并将根对象的名称更改为OctopartObject


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