Десериализация объектов с разными представлениями

#c# #.net #json.net

#c# #.net #json.net

Вопрос:

Я хочу десериализовать документ JSON, который содержит объекты одного и того же типа User, представленные двумя различными способами: как полное представление объекта или как ссылка на существующий пользовательский объект по имени:

 [
    {
        "User": "SomeUser"
    },
    {
        "User": {
            "Login": "SomeOtherUser",
            "Active": true
        }
    }
]
 

Легко написать конвертер, который обрабатывает пользовательские объекты, на которые ссылаются по имени:

 class User
{
    public string Id;
    public string Login;
    public bool Active;
}
    
class Document
{
    public User User;
}

class UserConverter : JsonConverter
{
    public UserConverter(Dictionary<string, User> users)
    {
        _users = users;
    }

    public override bool CanConvert(Type objectType)
    {
        var res = typeof(User).IsAssignableFrom(objectType);
        return res;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var name = ((User)value)?.Login;
        writer.WriteValue(name);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var name = reader.Value?.ToString();
        if (string.IsNullOrEmpty(name))
            return null;
        _users.TryGetValue(name, out var res);
        return res;
    }

    readonly Dictionary<string, User> _users;
}
// ....
    static void Test2()
    {
        var settings = new JsonSerializerSettings
        {
            MissingMemberHandling = MissingMemberHandling.Error,
            Converters =
            {
                new UserConverter(new Dictionary<string, User> { { "SomeUser", new User() }})
            }
        };
        var data = JsonConvert.DeserializeObject<List<Document>>("[{"User": "SomeUser"}, {"User": {"Login": "SomeUser","Active": true}}]", settings);
    }
 

Однако это приводит к ошибкам десериализации при обнаружении полных пользовательских объектов: «Newtonsoft.Json.Исключение JsonSerializationException: «Не удалось найти элемент «Login» для объекта типа «Document». Путь ‘[1].Пользователь.Вход ‘»
Есть идеи, как заставить его поддерживать оба?

Комментарии:

1. Я не понимаю, почему вы кэшируете пользователей в словаре. Это не имеет отношения к вопросу и не является потокобезопасным, как реализовано.

2. Вы уверены, что ссылка всегда будет отображаться после полного представления объекта в файле? Кроме того, как это User выглядит? Есть ли у него общедоступный конструктор без параметров?

3. Что за ошибка появляется?

4. Кроме того, при сериализации ваш текущий конвертер всегда будет записывать имя пользователя и никогда не будет записывать полное представление объекта. Это то, чего вы хотите?

5. @X39 смотрите обновление