Deserialize JSON with Known and Unknown Fields

Deserialize json with known and unknown fields

An even easier option to tackling this problem would be to use the JsonExtensionDataAttribute from JSON .NET

public class MyClass
{
// known field
public decimal TaxRate { get; set; }

// extra fields
[JsonExtensionData]
private IDictionary<string, JToken> _extraStuff;
}

There's a sample of this on the project blog here

UPDATE Please note this requires JSON .NET v5 release 5 and above

Deserialize Json with unknown fields / properties

You can not deserialize an undefined structure into a defined structure. Obviously this is not possible as fixed classes cannot be amended with new properties at runtime.

Anonymous objects to the help, sample from JSON.NET's documentation:

var definition = new { Name = "" };

string json1 = @"{'Name':'James'}";
var customer1 = JsonConvert.DeserializeAnonymousType(json1, definition);

Console.WriteLine(customer1.Name);
// James

string json2 = @"{'Name':'Mike'}";
var customer2 = JsonConvert.DeserializeAnonymousType(json2, definition);

Console.WriteLine(customer2.Name);
// Mike

Link to JSON.NET samples

How can I deserialize a JSON to a Java class which has known mandatory fields, but can have several unknown fields?

You can use JsonAnySetter annotation:

class PersonDTO {

@JsonProperty("first_name")
private String firstName;

@JsonProperty("last_name")
private String lastName;

private Map<String, String> extras = new HashMap<>();

@JsonAnySetter
public void setExtras(String name, String value) {
this.extras.put(name, value);
}

// No args constructor
// Getters
// Setters
}

See also:

  • JSON Jackson deserialization multiple keys into same field
  • Adding a dynamic json property as java pojo for jackson
  • How to use dynamic property names for a Json object

Deserialise JSON with unknown fields

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

spray json in scala: deserializing json with unknown fields without losing them

In order to do that, you need to write your own formatter for PersonalDataFormat:

case class Person(first: String, last: String)
case class Address(line: String, city: String)
case class Contact(phone: String, address: Address)
case class PersonalData(person: Person, contact: Contact, extra: Map[String, JsValue])
case class Entity(personalData: PersonalData)

implicit val personFormat = jsonFormat2(Person)
implicit val addressFormat = jsonFormat2(Address)
implicit val contactFormat = jsonFormat2(Contact)
implicit object PersonalDataFormat extends RootJsonFormat[PersonalData] {
override def read(json: JsValue): PersonalData = {
val fields = json.asJsObject.fields
val person = fields.get("person").map(_.convertTo[Person]).getOrElse(???) // Do error handling instead of ???
val contact = fields.get("contact").map(_.convertTo[Contact]).getOrElse(???) // Do error handling instead of ???
PersonalData(person, contact, fields - "person" - "contact")
}

override def write(personalData: PersonalData): JsValue = {
JsObject(personalData.extra ++ ("person" -> personalData.person.toJson, "contact" -> personalData.contact.toJson))
}
}

implicit val entityFormat = jsonFormat1(Entity)

val jsonResult = jsonString.parseJson.convertTo[Entity]

The result is:

Entity(PersonalData(Person(first_name,last_name),Contact(1111111,Address(123 Main St,New York)),Map(xx -> "yy", zz -> {"aa":"aa","bb":"bb","cc":{}})))

(Assuming the json is not exactly the json above, but a valid similar one)

Code run in Scastie

Parse JSON with mixed known/unknown fields in JAVA

You can use POJO with @JsonAnySetter method annotation. And you can actually even perform computations/optimisations in this method if you need them.

public class Simple {
private String A;
private String B;
private Map other = new HashMap<String,Object>();

// "any getter" needed for serialization
@JsonAnyGetter
public Map any() {
return other;
}

// "any setter" needed for deserialization
@JsonAnySetter
public void set(String name, Object value) {
other.put(name, value);
}

// getter and setter for A and B
}

Deserializing JSON with unknown fields

the JSON recieved seems to be a dictionnary..

did you try to deseserialize as a Dictionnary<string,Album> ?



Related Topics



Leave a reply



Submit