Десериализация сложного json объекта c# - PullRequest
1 голос
/ 15 января 2020

У меня есть такой json объект:

{
        "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"
                }
            ]
        ]
    }

До этого я всегда использовал var obj = JsonConvert.DeserializeObject<MyDistClass>(json); Но я не понимаю, как описать эту сущность:

"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"
            }
        ]
    ]

Я попробовал все классы, которые знал, даже попытался заменить кортеж, но безрезультатно. Помогите мне, пожалуйста.

Ответы [ 2 ]

4 голосов
/ 15 января 2020

Я бы, вероятно, использовал Json. NET, чтобы проанализировать JSON в JObject, а затем извлечь соответствующие значения из этого - используя JToken.ToObject<>, где это возможно.

Вот пример код, который, кажется, работает, но требует много проверки и общего исправления. Это больше о демонстрации идеи, чем что-либо еще:

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; }
}
2 голосов
/ 15 января 2020

Я смог сгенерировать конвертер с

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

Кажется, работает довольно хорошо.
Вот код :

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

Десериализовать так:

var myObject = JsonConvert.DeserializeObject<Request>(json, Converter2.Settings);
...