0

Given some json that looks like

{
  "id":"123_678",
  "fs_123_678":
   {
     "title":"Apple Parts",
     "data": 134
   }
}

I'd like to deserialize it into the following classes

public class Result
{
   public string Id {get; set;}
   public ResultInfo Info {get; set;}
}

public class ResultInfo
{
   public string Title {get; set;}
   public int Data {get; set;}
}

... but that second property (fs_123_678) is not a fixed name. I've looked at other similar posts on SO here, and the common solution is to use a IDictionary<string, MyPoco> however, that only works when the variable data is not at the root level. The name of the property is 100% predictable, and I could describe in code what it will be for a given JSON response based off of values used in the source HTTP request. The trick is, how do I let the JSON deseralizer in on the business rule around what it will be called for this given deserialization? I basically need something that would act like [JsonProperty(PropertyName = *insert logic here*)] (which of course isn't possible).

What's a reasonable way to approach this, all while retaining the "for free" strong-typed deserialization of the rest of the object?

ckittel
  • 6,478
  • 3
  • 41
  • 71
  • I'd write a custom deserializer (JSON.NET has a great article about it on their site). – Sam Axe Aug 23 '17 at 00:27
  • @SamAxe awesome, I was looking in the "Serializing and Deserializing JSON" section (https://www.newtonsoft.com/json/help/html/SerializingJSON.htm) and I didn't see anything that would help me out - but maybe I missed it? Got a handy dandy link by chance? – ckittel Aug 23 '17 at 00:55
  • You could use `[JsonTypedExtensionData]` and `TypedExtensionDataConverter` from [How to deserialize a child object with dynamic (numeric) key names?](https://stackoverflow.com/a/40094403/3744182). Or you could write a custom [`JsonConverter`](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm) for `Result` as shown in, say, [this answer](https://stackoverflow.com/a/23017892/3744182) or [this one](https://stackoverflow.com/a/30179162/3744182). – dbc Aug 23 '17 at 01:17
  • https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm – Sam Axe Aug 23 '17 at 04:31
  • Can someone link to the "similar" posts? I've also searched, but I can't find them, and it sounds like I can use dictionaries but its not clear how to me. – James Hurley Jul 20 '22 at 12:58
  • Potentially this one https://stackoverflow.com/questions/47507667/deserialize-json-with-variable-property-names – James Hurley Jul 20 '22 at 13:18

1 Answers1

2

Based on the links that dbc provided in comments, I found that this solution appears to be functional, but open to other suggestions. I'm bascially taking the current reader stream, loading it as a JObject then replacing the original JProperty with another that has the name my model is expecting.

public class MyCustomResultConverter : JsonConverter
{
    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JObject.Load(reader);
        var formData = obj.SelectToken("fs_" + obj.SelectToken("id").Value<string>());
        formData.Parent.Replace(new JProperty("info", formData));

        existingValue = existingValue ?? new FormActionResult();
        serializer.Populate(obj.CreateReader(), existingValue);
        return existingValue;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(Result).GetTypeInfo().IsAssignableFrom(objectType);
    }
}

[JsonConverter(typeof(MyCustomResultConverter))]
public class Result
{
   public string Id {get; set;}
   public ResultInfo Info {get; set;}
}
ckittel
  • 6,478
  • 3
  • 41
  • 71