Десериализовать json с помощью JSON.NET, но сохранить исходный json определенного класса в дополнительном поле

У меня есть сторонний поставщик данных с такой вложенной структурой данных

{
    "response": {
        "...": "...",
        "transactions": [
            {
                "well-known-field": 123,
                "new-unknown-field": 456,
            }
        ]
    }
}

Итак, у меня есть соответствующая структура классов для десериализации. На уровне транзакции у класса есть только хорошо известные поля. Но для каждой транзакции есть несколько динамических полей или просто новые поля, которые я предпочитаю иметь хотя бы в журналах или даже в документе mongodb. Я знал, что могу поставить JRaw для любого из этих полей, но я не знаю имен будущих полей и просто предпочитаю иметь полную необработанную транзакцию. Можно ли иметь дополнительный JObject самой транзакции внутри класса Transaction?

Полагаю, я могу сделать это путем десериализации через вторую структуру данных, где список транзакций - это просто List<JObject>, а затем просто перейти к каждой транзакции в первом результате и установить поле JObject Raw из второго результата, но это выглядит глупо. Может быть, есть какое-то расширение или встроенная возможность добиться этого в Newtonsoft JSON.Net за один проход?

public class Transaction
{
    public decimal WellKnownField { get; set; }
    public JObject RawTransaction { get; set; }
}

UPD: Думаю, лучше лучше string RawTransaction. Это было бы идеально для диагностики сериализации, для регистрации исходного необработанного json и для попыток повторно применить саму сериализацию в случае, если мы изменим некоторые атрибуты или настройки или значения по умолчанию.

Вы можете создавать типы динамически в C# (через класс TypeBuilder), но это не рекомендуется, поэтому в этом случае вам лучше придерживаться пары ключ-значение. Это было бы более производительно и менее сложно.

Transcendent 02.06.2018 22:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
813
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я бы порекомендовал вам сделать так, чтобы ваш класс Transaction выглядел так:

public class Transaction {

    [JsonProperty("id")]
    public String Id {get; set;}
    [JsonProperty("amount")]
    public decimal Amount {get; set;}
    ...
    [JsonExtensionData]
    public Dictionary<String,Object> AdditionalData {get; set;}
}

Таким образом, вы можете извлечь выгоду как из информации Strongly Typed, которая известна во время компиляции, так и из той, которая является динамической.

Я думаю, вы забыли атрибут [JsonExtensionData]. Да, это действительно очень близко ... За исключением того, что мне лучше иметь полный объект. Это связано с эволюцией класса транзакции. Прежде чем я добавлю новое поле в класс, у меня будет «новое-неизвестное-поле» в дополнительных данных, но после того, как я добавлю его, оно не будет отображаться в новых входящих транзакциях. И это усложняет логику обновления. Также - я понял, что могу поместить этот атрибут в поле JObject (потому что это IDictionary <string, JToken>)

Dmitry Gusarov 02.06.2018 22:22

@DmitryGusarov: 'Я думаю, ты забыл атрибут [JsonExtensionData].': Да, верно, я забыл об этом: P!

Transcendent 02.06.2018 22:26

В конце концов я вернулся к [JsonExtensionData]. Я сделал это, когда понял, что могу сделать то же самое при вставке в mongodb с помощью [BsonExtraElements]. Итак, все мои необъявленные столбцы попадают в поле [JsonExtensionData] [BsonIgnore], а затем конвертируются в получатель поля [BsonExtraElements] [JsonIgnore]. Это полностью решает мою первоначальную задачу - у меня есть весь динамический объект в базе данных, и я могу постепенно объявлять известные поля в C# без обновлений базы данных.

Dmitry Gusarov 16.06.2018 15:18

@DmitryGusarov: Mongo - отличный выбор, вы также научили меня кое-чему, так как я не знал о [BsonExtraElements]. Спасибо тебе за это :)

Transcendent 17.06.2018 18:54

Вот что я в итоге придумал:

public class TransactionResponse
{
    [JsonExtensionData]
    [JsonIgnore]
    public JObject AdditionalData { get; set; }

    [JsonIgnore]
    public JValue RawTransaction { get; set; }

    [OnDeserialized]
    internal void OnDeserialized(StreamingContext ctx)
    {
        var ser = (JValue)JsonConvert.SerializeObject(this);
        AdditionalData.Merge(ser);
        RawTransaction = ser;
    }

    public decimal Amount { get; set; }

Это дает полное представление об исходном объекте, и я даже могу поместить его в mongo в виде строки, используя RawTransaction.ToString() для любого будущего анализа. Не идеально, потому что он токенизирован, и я не вижу исходных пробелов и cr lf в json, но это, вероятно, слишком ...

Также мне не нравится, что я получаю целое число 0 и логическое значение false даже для тех полей, которые никогда не были в исходном json ... :(

Dmitry Gusarov 02.06.2018 23:01

Другие вопросы по теме