Dmitry Gusarov
Dmitry Gusarov

Reputation: 1639

Deserialize json with JSON.NET, but keep original json of particular class on additional field

I have some 3rd party data provider with a nested data structure like this

{
    "response": {
        "...": "...",
        "transactions": [
            {
                "well-known-field": 123,
                "new-unknown-field": 456,
            }
        ]
    }
}

So, I have a corresponding class structure for deserialization. On a transaction level the class have only well-known fields. But on each transaction there are some dynamic fields or just new fields that I prefer to have at least in logs or even in mongodb document. I known I can put JRaw for any of those fields, but I don't know the names of future fields and I just prefer to have full raw transaction itself. Is it possible to have additional JObject of transaction itself inside Transaction class?

I suppose, I can do this by deserializing via second data structure where List of transactions is just a List<JObject> and then just go foreach transaction in first result and set a JObject Raw field from second result but that looks silly. May be there are some extension or built-in ability to achieve that in Newtonsoft JSON.Net in one pass?

public class Transaction
{
    public decimal WellKnownField { get; set; }
    public JObject RawTransaction { get; set; }
}

UPD: I think I'd better prefer string RawTransaction instead. That would be perfect for serialization diagnostics, for logging original raw json and for attempts to re-apply serialization itself in case if we change some attributes or settings, or default values.

Upvotes: 3

Views: 1785

Answers (2)

Dmitry Gusarov
Dmitry Gusarov

Reputation: 1639

This is what I eventually came up with:

public class TransactionResponse
{
    [JsonExtensionData]
    [JsonIgnore]
    public JObject AdditionalData { get; set; }

    [JsonIgnore]
    public JValue RawTransaction { get; set; }

    [OnDeserialized]
    internal void OnDeserialized(StreamingContext ctx)
    {
        var ser = (JValue)JsonConvert.SerializeObject(this);
        AdditionalData.Merge(ser);
        RawTransaction = ser;
    }

    public decimal Amount { get; set; }

That gives a full view of original object, and I can even put it to mongo as a string using RawTransaction.ToString() for any future analysis. Not perfect because it is tokenized and I don't see the original whitespaces and cr lf in json but that's probably too much...

Upvotes: 1

Transcendent
Transcendent

Reputation: 5755

I would recommend you to make your Transaction class look like this:

public class Transaction {

    [JsonProperty("id")]
    public String Id {get; set;}
    [JsonProperty("amount")]
    public decimal Amount {get; set;}
    ...
    [JsonExtensionData]
    public Dictionary<String,Object> AdditionalData {get; set;}
}

This way you can both benefit from Strongly Typed information which are known at compile time and those which are dynamic.

Upvotes: 5

Related Questions