Одновременная десериализация нескольких типов и сглаживание объекта - PullRequest
0 голосов
/ 13 июля 2020

Учитывая следующее JSON:

[
  {
    "ticker": "AAPL",
    "name": "Apple Inc.",
    "market": "STOCKS",
    "locale": "US",
    "currency": "USD",
    "active": true,
    "primaryExch": "NGS",
    "type": "cs",
    "codes": {
      "cik": "0000320193",
      "figiuid": "EQ0010169500001000",
      "scfigi": "BBG001S5N8V8",
      "cfigi": "BBG000B9XRY4",
      "figi": "BBG000B9Y5X2"
    },
    "updated": "2019-01-15T05:21:28.437Z",
    "url": "https://api.polygon.io/v2/reference/tickers/AAPL"
  },
  {
    "ticker": "$AEDAUD",
    "name": "United Arab Emirates dirham - Australian dollar",
    "market": "FX",
    "locale": "G",
    "currency": "AUD",
    "active": true,
    "primaryExch": "FX",
    "updated": "2019-01-25T00:00:00.000Z",
    "attrs": {
      "currencyName": "Australian dollar,",
      "currency": "AUD,",
      "baseName": "United Arab Emirates dirham,",
      "base": "AED"
    },
    "url": "https://api.polygon.io/v2/tickers/$AEDAUD"
  },
  { /* another stock */ },
  { /* another stock */ },
  { /* another FX*/ },
  { /* another stock */ },
  { /* another FX*/ }
  /* and so forth */
]

I wi sh для десериализации в общий список типа List<Ticker>:

class Ticker
{
   string Ticker {get; set;}
   string Name {get; set;}
   MarketEnum Market {get; set;}
   LocaleEnum Locale {get; set;}
   CurrencyEnum Currency {get; set;}
   bool Active {get; set;}
   PrimaryExchEnum PrimaryExch {get; set;}
   DateTimeOffset Updated {get; set;}
}

class Stock : Ticker
{
  int Type {get; set;}
  string CIK {get; set;}
  string FIGIUID {get; set;}
  string SCFIGI {get; set;}
  string CFIGI {get; set;}
  string FIGI {get; set;}
}

class ForeignExchange : Ticker
{
   BaseCurrencyEnum BaseCurrency {get; set;}
}

У меня есть следующий код :

class TickerConvertor : CustomCreationConverter<Ticker>
{
    public override Ticker Create(Type objectType)
    {
        throw new NotImplementedException();
    }

    public Ticker Create(Type objectType, JObject jObject)
    {
        var type = (PrimaryExch)(int)jObject.Property("PrimaryExch");

        if (type == PrimaryExch.FX)
            return new ForeignExchange();
        else
            return new Stock();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        
        JObject jObject = JObject.Load(reader); // Load JObject from stream 
        var target = Create(objectType, jObject); // Create target object based on JObject 
        serializer.Populate(jObject.CreateReader(), target); // Populate the object properties 

        return target;
    }
}

Но я не уверен, как бы я поступил со сглаживанием подструктуры codes для типа Ticker и подструктуры attrs для типа ForeignExchange.

1 Ответ

1 голос
/ 14 июля 2020

Чтобы обрабатывать как создание правильного подкласса Ticker, так и сглаживание подструктуры из JSON, вы можете реализовать пользовательский JsonConverter следующим образом:

class TickerConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Ticker).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);

        Ticker ticker;
        JObject childObj;
        if (obj["market"].ToObject<MarketEnum>(serializer) == MarketEnum.FX)
        {
            ticker = new ForeignExchange();
            childObj = (JObject)obj["attrs"];
        }
        else
        {
            ticker = new Stock();
            childObj = (JObject)obj["codes"];
        }

        // populate common properties from the main object
        serializer.Populate(obj.CreateReader(), ticker); 

        // also populate from the selected child object
        serializer.Populate(childObj.CreateReader(), ticker); 

        return ticker;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To используйте конвертер, вы можете либо пометить класс Ticker с помощью [JsonConverter(typeof(TickerConverter))], либо передать преобразователь в JsonConvert.DeserializeObject().

Но подождите, в ваших классах модели потребуются некоторые другие изменения, прежде чем все заработает:

  1. Все ваши свойства должны быть public.
  2. Имя свойства не может совпадать с именем класса, в котором оно находится. В вашем случае оскорбительное свойство это Ticker. Вы можете переименовать класс (например, BaseTicker) или переименовать свойство (например, Symbol). Но если вы переименуете свойство, вам нужно будет пометить его [JsonProperty("ticker")], чтобы сохранить сопоставление с JSON.
  3. В вашем классе Stock свойство Type объявлено как int, но в JSON это строка. Это вызовет ошибку во время десериализации. Свойство необходимо объявить как строку.
  4. Вы определили различные enum свойства для многих свойств, которые являются строками в JSON. Вы получите ошибку преобразования во время десериализации, если вы не используете StringEnumConverter с каждым перечислением. Вы можете сделать это, пометив свойства в ваших классах или сами перечисления с помощью [JsonConverter(typeof(StringEnumConverter))]. Я бы выбрал последнее. Но еще одна вещь, которую следует учитывать: StringEnumConverter, который поставляется с Json. Net, очень строг. Если в JSON появляется значение, для которого нет соответствующего значения, определенного в перечислении, преобразователь выдаст ошибку. Поэтому вам нужно убедиться, что каждое перечисление имеет полный набор всех возможных значений, с которыми вы можете столкнуться для него. Если вы хотите сделать его более терпимым, вы можете вместо этого использовать TolerantEnumConverter, показанный в . Как я могу игнорировать неизвестные значения перечисления во время json десериализации?
  5. Имя BaseCurrency свойство в классе ForeignExchange не соответствует JSON. В JSON он называется base. Поэтому вам нужно будет пометить это свойство с помощью [JsonProperty("base")].

С внесенными выше изменениями классы и перечисления должны выглядеть примерно так:

[JsonConverter(typeof(TickerConverter))]
class Ticker
{
    [JsonProperty("ticker")]
    public string Symbol { get; set; }
    public string Name { get; set; }
    public MarketEnum Market { get; set; }
    public LocaleEnum Locale { get; set; }
    public CurrencyEnum Currency { get; set; }
    public bool Active { get; set; }
    public PrimaryExchEnum PrimaryExch { get; set; }
    public DateTimeOffset Updated { get; set; }
}

class Stock : Ticker
{
    public string Type { get; set; }
    public string CIK { get; set; }
    public string FIGIUID { get; set; }
    public string SCFIGI { get; set; }
    public string CFIGI { get; set; }
    public string FIGI { get; set; }
}

class ForeignExchange : Ticker
{
    [JsonProperty("base")]
    public BaseCurrencyEnum BaseCurrency { get; set; }
}

[JsonConverter(typeof(StringEnumConverter))]
enum MarketEnum { STOCKS, FX }

[JsonConverter(typeof(StringEnumConverter))]
enum LocaleEnum { US, G }

[JsonConverter(typeof(StringEnumConverter))]
enum CurrencyEnum { USD, AUD, EUR }

[JsonConverter(typeof(StringEnumConverter))]
enum PrimaryExchEnum { NGS, FX }

[JsonConverter(typeof(StringEnumConverter))]
enum BaseCurrencyEnum { AED }

Вот рабочая демонстрация: https://dotnetfiddle.net/0WQLHT

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...