1

I have in such an json object:

{
        "status": "Success",
        "value": [
            [
                "2019-10-21T11:48:00Z",
                "010719/360",
                "hodler@hodler.hodler",
                {
                    "role": "hodler",
                    "company": "Proximus",
                    "first_name": "Ivan",
                    "last_name": "Holms",
                    "modification_date": "2019-10-21 10:33:39"
                }
            ]
        ]
    }

Before that, I always used var obj = JsonConvert.DeserializeObject<MyDistClass>(json); But I do not understand how to describe this entity:

"value": [
        [
            "2019-10-21T11:48:00Z",
            "010719/360",
            "holder@holder.holder",
            {
                "role": "holder",
                "company": "Proximus",
                "first_name": "Marc",
                "last_name": "Neukirch",
                "modification_date": "2019-10-21T10:33:39"
            }
        ]
    ]

I tried all the classes I knew, even tried to substitute the tuple, but without result. Help me please.

  • I'm assuming, that in your second snippet (that starts with `"value"`) you simply didn't provide the previous snippet's header/footer - ala `{ "status": "Success",}` and then at the end a closing bracket `}`? Just want to be clear about the structure of your JSON ahead of any issues. – gravity Jan 15 '20 at 17:14
  • Are you in control of this JSON at all? The structure is pretty unfortunate at the moment. You'd probably need a custom serializer, or to go via `JObject` or similar. If you can redesign the JSON, that would make life much simpler. – Jon Skeet Jan 15 '20 at 17:19
  • @JonSkeet This is not my object, I get it according to the documentation – Роман Тимохов Jan 15 '20 at 17:23
  • I'm not quite sure what you mean by that, but should we understand that you definitely can't change the JSON structure? – Jon Skeet Jan 15 '20 at 17:31
  • @JonSkeet No, I can’t influence her. – Роман Тимохов Jan 15 '20 at 17:33
  • Have a look at this one. It seems similar to what you're trying to achieve, you just have a tuple with 4 items. [Deserialize JSON array of arrays into List of Tuples using Newtonsoft](https://stackoverflow.com/questions/45488785/deserialize-json-array-of-arrays-into-list-of-tuples-using-newtonsoft) – Grabofus Jan 15 '20 at 18:06

2 Answers2

4

I would probably use Json.NET to parse the JSON to a JObject, then extract the relevant values from that - using JToken.ToObject<> where possible.

Here's some sample code that appears to work - but needs a lot of validation and general tidying up. It's more about demonstrating the idea than anything else:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        string json = File.ReadAllText("test.json");
        Result result = Result.FromJson(json);
        Console.WriteLine(result.Status);
        Console.WriteLine(result.Values.Count);
        Console.WriteLine(result.Values[0].User.FirstName);
    }
}

public sealed class Result
{
    public string Status { get; }
    public IReadOnlyList<ResultValue> Values { get; }

    private Result(string status, IReadOnlyList<ResultValue> values) =>
        (Status, Values) = (status, values);

    public static Result FromJson(string json)
    {
        JObject parsed = JObject.Parse(json);
        string status = (string) parsed["status"];
        JArray array = (JArray) parsed["value"];
        var values = array.Select(ResultValue.FromJToken).ToList().AsReadOnly();
        return new Result(status, values);
    }
}

public sealed class ResultValue
{
    public DateTime Timestamp { get; }
    public string Id { get; }
    public string Email { get; }
    public User User { get; }

    private ResultValue(DateTime timestamp, string id, string email, User user) =>
        (Timestamp, Id, Email, User) = (timestamp, id, email, user);

    internal static ResultValue FromJToken(JToken token)
    {
        JArray array = (JArray) token;
        DateTime timestamp = (DateTime) array[0];
        string id = (string) array[1];
        string email = (string) array[2];
        User user = array[3].ToObject<User>();
        return new ResultValue(timestamp, id, email, user);
    }
}

// TODO: Make this immutable, or everything else immutable
public sealed class User
{
    [JsonProperty("role")]
    public string Role { get; set; }
    [JsonProperty("company")]
    public string Company { get; set; }
    [JsonProperty("first_name")]
    public string FirstName { get; set; }
    [JsonProperty("last_name")]
    public string LastName { get; set; }
    [JsonProperty("modification_date")]
    public DateTime ModificationDate { get; set; }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

I was able to generate a converter with

https://app.quicktype.io/?l=csharp

Seems to work pretty well.
Here's the Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace QuickType
{
    public partial class Request
    {
        [JsonProperty("status")]
        public string Status { get; set; }

        [JsonProperty("value")]
        public ValueElement[][] Value { get; set; }
    }

    public partial class ValueClass
    {
        [JsonProperty("role")]
        public string Role { get; set; }

        [JsonProperty("company")]
        public string Company { get; set; }

        [JsonProperty("first_name")]
        public string FirstName { get; set; }

        [JsonProperty("last_name")]
        public string LastName { get; set; }

        [JsonProperty("modification_date")]
        public DateTimeOffset ModificationDate { get; set; }
    }

    public partial struct ValueElement
    {
        public string String;
        public ValueClass ValueClass;

        public static implicit operator ValueElement(string String) => new ValueElement { String = String };
        public static implicit operator ValueElement(ValueClass ValueClass) => new ValueElement { ValueClass = ValueClass };
    }

    internal static class Converter2
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                ValueElementConverter.Singleton,
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }

    internal class ValueElementConverter : JsonConverter
    {
        public override bool CanConvert(Type t) => t == typeof(ValueElement) || t == typeof(ValueElement?);

        public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
        {
            switch (reader.TokenType)
            {
                case JsonToken.String:
                case JsonToken.Date:
                    var stringValue = serializer.Deserialize<string>(reader);
                    return new ValueElement { String = stringValue };
                case JsonToken.StartObject:
                    var objectValue = serializer.Deserialize<ValueClass>(reader);
                    return new ValueElement { ValueClass = objectValue };
            }
            throw new Exception("Cannot unmarshal type ValueElement");
        }

        public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
        {
            var value = (ValueElement)untypedValue;
            if (value.String != null)
            {
                serializer.Serialize(writer, value.String);
                return;
            }
            if (value.ValueClass != null)
            {
                serializer.Serialize(writer, value.ValueClass);
                return;
            }
            throw new Exception("Cannot marshal type ValueElement");
        }

        public static readonly ValueElementConverter Singleton = new ValueElementConverter();
    }
}

Deserialize like this:

var myObject = JsonConvert.DeserializeObject<Request>(json, Converter2.Settings);
Charles
  • 2,721
  • 1
  • 9
  • 15