2

I have in such an json object:

{
        "status": "Success",
        "value": [
            [
                "2019-10-21T11:48:00Z",
                "010719/360",
                "[email protected]",
                {
                    "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",
            "[email protected]",
            {
                "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.

6
  • 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. Commented Jan 15, 2020 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. Commented Jan 15, 2020 at 17:19
  • @JonSkeet This is not my object, I get it according to the documentation Commented Jan 15, 2020 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? Commented Jan 15, 2020 at 17:31
  • @JonSkeet No, I can’t influence her. Commented Jan 15, 2020 at 17:33

2 Answers 2

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; }
}
Sign up to request clarification or add additional context in comments.

Comments

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);

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.