-2

I need a JsonConverter that can handle the following Json Structure. I always want back the segment model for each object in the "segments" collection in the Json. The problem I am having is how to handle the multiple structures in the Json. I am somewhat familiar with writing JsonConverters but this one has me stumped. I asked the partner we are working with if it was possible to standardize the response and was told no.

{ 
    "segments": [ 
        { 
            "headers" : 
            { 
                "agent":"000000000049",
                "end_use":"consumer"
            }, 
            "data" : { 
                "vsd_id": "FI_444_56", 
                "name": "Filler Segment 4inx4inx2in", 
                "price":"00900", 
                "lead_time":"4-6"
                "certificate": "BR22"
            }
        }, 
        {
            "vsd_id": "RB_190_01", 
            "name": "Rod Backer 94in", 
            "price":"05000", 
            "lead_time":"4-6"
            "certificate": "BR23"
        }
    ]
}

public class SegmentRepository { 

    private Newtonsoft.Json.JsonSerializer _serializer;
    private HttpClient _client;

    public async Task<IList<Segment>> GetSegments(....) 
    { 
        // more code here that builds the requestMessage. not important

        using (var responseMessage = await _client.SendAsync(requestMessage))
        using (var content = await responseMessage.Content.ReadAsStreamAsync())
        using (var textReader = new StreamReader(content))
        using (var reader = new JsonTextReader(textReader))
        {
            var response = _serializer.Deserialize<Response>(reader);
            return response.Segments;
        }
    }
}

public class Response 
{ 
    [JsonProperty("segments")]
    public List<Segment> Segments {get;set;}
}

public class Segment 
{ 
    [JsonProperty("headers")]
    public Dictionary<string,string> Headers {get;set;}

    [JsonProperty("data")]
    public SegmentData Data {get;set;}
}

public class SegmentData 
{ 
    [JsonProperty("vsd_id")]
    public string Id {get;set;}
    
    [JsonProperty("name")]
    public string Name {get;set;}
    
    [JsonConverter(typeof(PriceConverter))]
    public Decimal Price {get;set;}
    
    [JsonProperty("lead_time")]
    [JsonConverter(typeof(LeadTime))]
    public LeadTime LeadTime {get;set;}

    [JsonProperty("certificate")]
    public string Certificate {get;set;}
}

NOTE: I have already asked the onwer of the API if they could standardize their responses and was told no.

Matt M
  • 592
  • 1
  • 5
  • 27
  • So then.. what's the question? The data structure looks normal to me and it looks like you've built your models just fine. – Andy Nov 08 '20 at 21:14
  • The Json changes between objects in the collection. take a look a the Json a bit closer. – Matt M Nov 08 '20 at 21:25
  • But does it change between actual objects. If not, then you have 3 separate distinct models. It's normal. – Andy Nov 08 '20 at 21:33
  • Make `Segment` and `SegmentData` inherit from some base class, say `SegmentBase`. Then you can create a custom `JsonConverter` as shown in [Deserializing polymorphic json classes without type information using json.net](https://stackoverflow.com/q/19307752/3744182), [Json.Net Serialization of Type with Polymorphic Child Object](https://stackoverflow.com/q/29528648/3744182) or [How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?](https://stackoverflow.com/q/8030538/3744182). – dbc Nov 10 '20 at 01:22
  • Basically the converter preloads into a `JObject` then checks the properties present to determine whether to deserialize to a `Segment` or `SegmentData`. – dbc Nov 10 '20 at 01:23

1 Answers1

0

I figured out how to do it. It's quite a simple solution once I figured it out. The piece I was missing for awhile was that I should decorate the class with the JsonCoverter attribute instead of a property.

[JsonConverter(typeof(SegmentsConverter))]
public class Segment
{
    [JsonProperty("headers")]
    public Dictionary<string, string> Headers { get; set; }

    [JsonProperty("data")]
    public SegmentData Data { get; set; }
}


public class SegmentsConverter : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        
        var jObject = JObject.Load(reader);

        if (FieldExists(jObject, "headers"))
        {
            var target = new Segment();
            serializer.Populate(jObject.CreateReader(), target);
            return target;
        }
        else
        {
            var container = new Segment();
            var data = new SegmentData();
            container.Data = data;
            serializer.Populate(jObject.CreateReader(), data);
            return container;
        }
    }


    private bool FieldExists(JObject jObject, string fieldName)
    {
        return jObject[fieldName] != null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
Matt M
  • 592
  • 1
  • 5
  • 27