c# - 同时反序列化多种类型并展平对象
问题描述
给定以下 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 */
]
我希望反序列化为一个常见的类型列表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
解决方案
要处理正确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();
}
}
要使用转换器,您可以使用 标记Ticker
类[JsonConverter(typeof(TickerConverter))]
或将转换器传递给JsonConvert.DeserializeObject()
.
但是等等,在你的模型类中需要一些其他的改变才能工作:
- 您的所有属性都必须是
public
. - 您不能将属性名称与其所在的类相同。在您的情况下,有问题的属性是
Ticker
. 您可以重命名类(例如BaseTicker
)或重命名属性(例如Symbol
)。但是,如果您重命名该属性,则需要将其标记为[JsonProperty("ticker")]
以保留到 JSON 的映射。 - 在您的
Stock
类中,该Type
属性被声明为,int
但在 JSON 中它是一个字符串。这将在反序列化期间导致错误。该属性需要声明为字符串。 - 您已经
enum
为 JSON 中的许多字符串定义了各种属性。StringEnumConverter
除非您对每个枚举使用 a,否则您将在反序列化期间收到转换错误。您可以通过使用 标记类中的属性或枚举本身来做到这一点[JsonConverter(typeof(StringEnumConverter))]
。我会选择后者。但要考虑的另一件事是:StringEnumConverter
Json.Net 附带的非常严格。如果 JSON 中出现的值在枚举中没有定义对应的值,则转换器将抛出错误。因此,您需要确保每个枚举都有一整套您可能会遇到的所有可能值。如果你想让它更宽容,你可以改用TolerantEnumConverter
所示的如何在 json 反序列化期间忽略未知的枚举值? 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
推荐阅读
- javascript - 如何将值设置为角度输入
- docker - 使用 docker-compose 从私有仓库通过 ssh 构建映像时主机密钥验证失败
- docfx - DocFx:如何抑制有关类型的某些信息(继承、构造函数、程序集等)
- python-3.x - Pandas:当没有分隔符时,如何将位串读入单独的列?
- animation - 如何在 Xamarin Forms 中创建此动画
- java - 如何使用日期时间格式
- python - 从字典列表中构建一个列表
- eclipse - Eclipse (Mars) 建模工具 - 无法打开“市场”或“安装详细信息”
- c# - 在 ASP.NET Core 中流式传输内存中生成的文件
- c# - .NET 无状态多重 PermitIf