0

I have a strange json, which:

  • I can not change it, it is from a third party
  • It can have up to 75 properties

A simple example of the json:

{
   "bookingcode":["ABC","DEF", "GHJ", "TEST"],
   "referencenumber":[123, 456]
   "bookingdate":["22-07-2022T14:00:30", "23-11-2022T17:00:25"]
}

I am only interested in the last value of the array and want to deserialize into a class called Booking, so in this case bookingcode="TEST" and bookingdate="23-11-2022T17:00:25".

A solution would be define a model:

public class Booking
{
    public string BookingCode { get; set; }
    public int ReferenceNumber { get; set; }
    public DateTime BookingDate { get; set;}
}

public class BookingInput
{
     public List<string> BookingCode { get; set;}
     public List<int> ReferenceNumber { get; set; }
     public List<DateTime> BookingDate { get; set; }
}

var bookinginput = JsonSerializer.Deserialize<BookingInput>(jsonString);
var booking = new Booking
{
     BookingCode = bookinginput.BookingCode.Last(),
     ReferenceNumber = bookinginput.ReferenceNumber.Last(),
     ...
}

I think it is very tedious to write out all 75 properties in code like this. Is there a better solution in Json.net or System.Text.Json for this?

1
  • 1
    I might suggest a different approach. dofactory.com/net/proxy-design-pattern If your backend returns a (metaphor) "MyThingSuperDeep" object.. your just serialize that completely...and then you write a "MyThingLite" proxy (wrapper) object....to expose what you want. Obviously the "inner object" may be heavy...and are you dealing with a 100 "MyThing(s)" or a million MyThing(s). I would consider the proxy design pattern if you're dealing with low total MyThing(s). Commented Oct 19, 2022 at 12:47

4 Answers 4

1

Using NewtonSoft.Json, you can create your own JsonConverter. Below I have created a LastArrayItemJsonConverter, which takes the last item from an array of a certain type and returns that.

public class LastArrayItemJsonConverter<TItem> : JsonConverter<TItem>
{
    private string _formatString;
    
    public LastArrayItemJsonConverter()
    { }
    
    public LastArrayItemJsonConverter(string formatString)
    {
        _formatString = formatString;
    }
    
    public override TItem ReadJson(JsonReader reader, Type objectType, TItem existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (typeof(TItem) == typeof(DateTime) || typeof(TItem) == typeof(DateTime?))
            reader.DateFormatString = _formatString;
        
        TItem result = default;
        if (reader.TokenType != JsonToken.StartArray)
            return default;
        
        while (reader.Read() && reader.TokenType != JsonToken.EndArray)
            result = (TItem)Convert.ChangeType(reader.Value, typeof(TItem));
        
        return result;
    }

    public override void WriteJson(JsonWriter writer, TItem value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

By decorating your model, you can specify that the serializer should use the converter to convert the properties:

public class Booking
{
    [JsonConverter(typeof(LastArrayItemJsonConverter<string>))]
    public string BookingCode { get; set; }
    [JsonConverter(typeof(LastArrayItemJsonConverter<int>))]
    public int ReferenceNumber { get; set; }
    [JsonConverter(typeof(LastArrayItemJsonConverter<DateTime>), "dd-MM-yyyy\\THH:mm:ss")]
    public DateTime BookingDate { get; set; }
}

Now, the model's properties will be populated with the last values from the arrays. Deserialize the json using:

var booking = JsonConvert.DeserializeObject<Booking>(json)
Sign up to request clarification or add additional context in comments.

Comments

1

You can define a single Booking class which includes

  • the BookingInput's properties
  • and the last item retrieval logics as well
public class Booking
{
    [JsonIgnore]
    public string BookingCode => BookingCodes.Last();
    [JsonIgnore]
    public int ReferenceNumber => ReferenceNumbers.Last();
    [JsonIgnore]
    public DateTime BookingDate => BookingDates.Last();

    [JsonProperty("bookingcode")]
    public List<string> BookingCodes { get; set; } = new List<string>();
    [JsonProperty("referencenumber")]
    public List<int> ReferenceNumbers { get; set; } = new List<int>();
    [JsonProperty("bookingdate")]
    public List<DateTime> BookingDates { get; set; } = new List<DateTime>();
}

3 Comments

I am upvoting this. This is similar to the idea I propose (in the comments of the original question). My idea would hide-away the actual properties. But as I state in my comment, does the OP need "100, 1000" of these for "a million".
To the OP, other readers. I would be inclined to use LastOrDefault instead of just Last.....so avoid null-reference-exceptions. and perhaps ? null on the return data type. the next stuff is the "intention" .. but something like public int? ReferenceNumber => ReferenceNumbers.LastOrDefault();
@granadaCoder Thanks for the upvote. I agree that LastOrDefault would bring robustness.
0

if you want to use your custom c# class

    using Newtonsoft.Json;

    var jsonParsed = JObject.Parse(json);

    foreach (var arr in jsonParsed.Properties())
        jsonParsed[arr.Name] = arr.Value.LastOrDefault();

    Booking booking = jsonParsed.ToObject<Booking>();

but since you can have up to 75 properties, maybe it would be better to use a dictionary instead of a custom class

    Dictionary<string, object> bookingDict = new Dictionary<string, object>();

    foreach (var arr in jsonParsed.Properties())
        bookingDict.Add(arr.Name, arr.Value.LastOrDefault());

result

{
  "bookingcode": "TEST",
  "referencenumber": 456,
  "bookingdate": "23-11-2022T17:00:25"
}

Comments

0

It's better if you provide your request code too. did you add ReadAsStringAsync(); after the request?

public class Booking
{   
    [JsonProperty("bookingcode")]
    public List<string> BookingCodes { get; set; } = new List<string>();
  
    [JsonProperty("referencenumber")]
    public List<int> ReferenceNumbers { get; set; } = new List<int>();
    
    [JsonProperty("bookingdate")]
    public List<DateTime> BookingDates { get; set; } = new List<DateTime>();
}

And some web request sample

 using (var client = new HttpClient())
            {
                var response = await client.
                    GetAsync(apiEndpoint);
                var responseText = await response.Content.ReadAsStringAsync();
                var compareResult = JsonSerializer.Deserialize<Booking>(responseText)??new Booking();
                return compareResult;
            }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.