3

I am attempting to deserialise results from the OctoPart API (http://octopart.com/api/docs/v3/rest-api) using c# and json.net

There are no issues deserialising the majority of the data, however I am running troubles with the Part.specs attributes (http://octopart.com/api/docs/v3/rest-api#notes-part.specs) because the attributes change depending on the item returned.

Here is what the API says about Part.Specs

The specs attribute attached to Part instances is a JSON object mapping attribute short names (e.g. "voltage_rating_dc") to QualitativeValue and QuantitativeValue instances.
It's important to note that all value properties of (Qual|Quant)itativeValue objects are JSON arrays. The reason for this is to accomodate multi-valued attributes such as power supplies with multiple output voltages:

Because I want to maintain the class, I believe that I might need to implement a custom JConverter? I was looking at this question, but wasnt too sure how to apply to this example beccause the attributes can be deserialised into the QualitativeValue / QuantitativeValue classes

Here is my Part class

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; }
}

Here is an example result (PartsMatchResponse) from the API

{
    "__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
        }
    ]
}
Community
  • 1
  • 1
Spacko
  • 385
  • 1
  • 3
  • 14

2 Answers2

1

Yes, you will need a custom JsonConverter to solve this. Basically what you need to do is this:

  1. Define your QualitativeValue and QuantitativeValue classes to have a common base class (e.g. AbstractQValue) or interface.
  2. In your Parts class, make the specs property a Dictionary<string, AbstractQValue>. This will handle the changing attribute names.
  3. Create a custom JsonConverter to handle the instantiation of the concrete QualitativeValue or QuantitativeValue based on the __class__ attribute in the JSON. See this answer for an example of how to implement this.
  4. Lastly, when you do your deserialization, be sure to pass an instance of the custom JsonConverter into the JsonConvert.DeserializeObject method.

Demo

I had a little time, so I threw together a working example. Here are the class definitions for the data (I cut out most of the extraneous stuff for brevity):

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; }
}

Here is the custom JsonConverter class:

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();
    }
}

Here is a demo program showing how to use the converter when deserializing:

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
                }
            ]
        }";
}

And finally, here is the output of the above program:

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
Community
  • 1
  • 1
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
0

You'll need these classes to model the 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; }
}

Then using JSON.NET & .NET 4.5, do something like this.

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);

You'll have a nice object that should model the data received from the _address URI

I still haven't fully tested this, so there could be some issues. But I've been struggling for a few hours on this and finally found something that seems to be working. I'm sure it won't work for datasheets and any extra fields, as this only returns the basic. But I essentially just used this site to get the object models and changed the name of the root one to OctopartObject

Calvin
  • 710
  • 1
  • 10
  • 28