Utf8Json反序列化为基于标记字段的类型
Utf8Json deserialize to type based on marker field
使用 Json.NET - Newtonsoft
我已经成功地使用自定义合约反序列化器和 json 转换器到 select 基于标签的反序列化器(在下面的例子中 ev
)。
总而言之我希望通过 Utf8Json
实现相同的目标,详情如下:
// Stocks TRADE:
{
"ev": "T", // Event Type
"sym": "MSFT", // Symbol Ticker
"x": "4", // Exchange ID
"i": 12345, // Trade ID
"z": 3, // Tape ( 1=A 2=B 3=C)
"p": 114.125, // Price
"s": 100, // Trade Size
"c": [0, 12], // Trade Conditions
"t": 1536036818784 // Trade Timestamp ( Unix MS )
}
// Stocks QUOTE:
{
"ev": "Q", // Event Type
"sym": "MSFT", // Symbol Ticker
"bx": "4", // Bix Exchange ID
"bp": 114.125, // Bid Price
"bs": 100, // Bid Size
"ax": "7", // Ask Exchange ID
"ap": 114.128, // Ask Price
"as": 160, // Ask Size
"c": 0, // Quote Condition
"t": 1536036818784 // Quote Timestamp ( Unix MS )
}
// Stocks Aggregate:
{
"ev": "AM", // Event Type ( A = Second Agg, AM = Minute Agg )
"sym": "MSFT", // Symbol Ticker
"v": 10204, // Tick Volume
"av": 200304, // Accumulated Volume ( Today )
"op": 114.04, // Todays official opening price
"vw": 114.4040, // VWAP (Volume Weighted Average Price)
"o": 114.11, // Tick Open Price
"c": 114.14, // Tick Close Price
"h": 114.19, // Tick High Price
"l": 114.09, // Tick Low Price
"a": 114.1314, // Tick Average / VWAP Price
"s": 1536036818784, // Tick Start Timestamp ( Unix MS )
"e": 1536036818784, // Tick End Timestamp ( Unix MS )
}
以及可以包含上述任何类型的文本流:
[{"ev":"A","sym":"DAL","v":1,"av":1, ...snipped...},{"ev":"T","sym":"MSFT","p":114.11,"x":"4","s":67,"t":1586418423607, ...snipped... }]
并反序列化为:
class Message
{
List<Trade> Trades { get; set; }
List<Quote> Quotes { get; set; }
List<Aggregate> Aggs { get; set; }
}
我目前正在这样做,它有效,但怀疑与直接反序列化到 POCO 一样高效:
var array = JsonSerializer.Deserialize<dynamic>(@"[{""ev"":""A"",""sym"":""AAL"",""v"":1500,""av"":119037385,""op"":12.64,""vw"":13.1,""o"":13.1,""c"":13.1,""h"":13.1,""l"":13.1,""a"":12.6655,""z"":500,""s"":1586472438000,""e"":1586472439000},{""ev"":""A"",""sym"":""AAL"",""v"":6000,""av"":119043385,""op"":12.64,""vw"":13.1,""o"":13.1,""c"":13.1,""h"":13.1,""l"":13.1,""a"":12.6655,""z"":1000,""s"":1586472439000,""e"":1586472440000},{""ev"":""A"",""sym"":""AAL"",""v"":3000,""av"":119046385,""op"":12.64,""vw"":13.11,""o"":13.11,""c"":13.11,""h"":13.11,""l"":13.11,""a"":12.6655,""z"":1000,""s"":1586472440000,""e"":1586472441000}]");
foreach(var item in array)
{
if(item["ev"]=="A")
{
var aggregate = new OpenHighLowClose(
DateTimeOffset.FromUnixTimeMilliseconds((long)item["s"]),
(decimal)item["op"],
(decimal)item["h"],
(decimal)item["l"],
(decimal)item["c"],
(decimal)item["v"]);
}
else if(item["ev"=="AM"]) { }
else if(item["ev" == "T"]) { }
else if(item["ev" == "Q"]) { }
}
json.net的JsonConverter在Utf8Json中的等价物是什么,所以我可以根据EV 字段(以及 T、A、AM 或 Q 的关联字符串值)?
补充一下,我将从 JSON 转到 OpenHighLowClose POCO,然后我将通过消息包发送出去...有什么方法可以跳过这个中间步骤吗?
看起来像这样的消息怎么样:
class Message
{
List<IStockItem> Items { get; set; }
}
IStockItem 是一个接口,类 类型 Trade、Quote 和 Aggregate 继承自 IStockItem。
在您的自定义 JsonConverter 的 ReadJson 中,您将根据 ev
的值放置序列化条件
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
else if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize(reader, objectType);
}
else
{
JObject obj = JObject.Load(reader);
if (obj["ev"] != "A")
return // Logic for A
else if (obj["ev"] != "T")
return // Logic for T
else
return serializer.Deserialize(reader, objectType);
}
}
如果您可以假设 ev
字段在对象中始终排在第一位,则可以通过最少的分配和最少的额外解析来解决问题。 netcoreapp3.0以后,System.Text.Json
支持custom type converters,可以这样写:
[JsonConverter(typeof(MessageConverter))]
class Message
{
List<Trade> Trades { get; set; }
List<Quote> Quotes { get; set; }
List<Aggregate> Aggs { get; set; }
}
public class MessageConverter : JsonConverter<Message>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(Message).IsAssignableFrom(typeToConvert);
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var message = new Message();
message.Trades = new List<Trade>();
message.Quotes = new List<Quotes>();
message.Aggs = new List<Aggs>();
while(Expect(ref reader, JsonTokenType.StartObject, JsonTokenType.EndArray))
{
Expect(ref reader, JsonTokenType.PropertyName);
var propertyName = reader.GetString();
if (propertyName != "ev")
throw new JsonException();
switch(ExpectString(ref reader))
{
default:
throw new JsonException();
case "T":
var trade = new Trade();
while(Expect(ref reader, JsonTokenType.PropertyName, JsonTokenType.EndObject))
switch(reader.GetString())
{
default: throw new JsonException();
case "sym": trade.Symbol = ExpectString (ref reader); break;
case "p": trade.Price = ExpectDecimal(ref reader); break;
...
}
message.Trades.Add(trade);
break;
...
}
}
return message;
}
public override void Write(Utf8JsonWriter writer, Message message, JsonSerializerOptions options) =>
throw new NotSupportedException();
private void Expect(ref Utf8JsonReader reader, JsonTokenType t)
{
reader.Read();
if (reader.TokenType != t)
throw new JsonException();
}
private string ExpectString(ref Utf8JsonReader reader)
{
Expect(ref reader, JsonTokenType.String);
return reader.GetString();
}
private decimal ExpectDecimal(ref Utf8JsonReader reader)
{
Expect(ref reader, JsonTokenType.Number);
return reader.GetDecimal();
}
private bool Expect(ref Utf8JsonReader reader, JsonTokenType a, JsonTokenType b)
{
reader.Read();
if (reader.TokenType == a) return true ;
if (reader.TokenType == b) return false ;
throw new JsonException();
}
}
如果由于某种原因您不能使用 netcoreapp3.x,那么您将不得不放弃 JsonSerializer
用于外部 Message
和 运行 Read
直接方法。如果您愿意花费一些周期来避免手动反序列化内部对象,请在您的 属性 名称
上应用 JsonPropertyName
属性
struct Trade
{
[JsonPropertyName("sym")]
public string Symbol { get; set; }
[JsonPropertyName("p")]
public decimal Price { get; set; }
...
}
并像这样重写 Read
方法的外层循环:
while (Expect(ref reader, JsonTokenType.StartObject, JsonTokenType.EndArray))
{
// save reader state to peek the object type
var copy = reader;
Expect(ref copy, JsonTokenType.PropertyName);
var propertyName = copy.GetString();
if (propertyName != "ev")
throw new JsonException();
switch(ExpectString(ref copy))
{
default:
throw new JsonException();
case "T":
message.Trades.Add(JsonSerializer.Deserialize<Trade>(ref reader));
break;
//...
}
}
使用 Json.NET - Newtonsoft
我已经成功地使用自定义合约反序列化器和 json 转换器到 select 基于标签的反序列化器(在下面的例子中 ev
)。
总而言之我希望通过 Utf8Json
实现相同的目标,详情如下:
// Stocks TRADE:
{
"ev": "T", // Event Type
"sym": "MSFT", // Symbol Ticker
"x": "4", // Exchange ID
"i": 12345, // Trade ID
"z": 3, // Tape ( 1=A 2=B 3=C)
"p": 114.125, // Price
"s": 100, // Trade Size
"c": [0, 12], // Trade Conditions
"t": 1536036818784 // Trade Timestamp ( Unix MS )
}
// Stocks QUOTE:
{
"ev": "Q", // Event Type
"sym": "MSFT", // Symbol Ticker
"bx": "4", // Bix Exchange ID
"bp": 114.125, // Bid Price
"bs": 100, // Bid Size
"ax": "7", // Ask Exchange ID
"ap": 114.128, // Ask Price
"as": 160, // Ask Size
"c": 0, // Quote Condition
"t": 1536036818784 // Quote Timestamp ( Unix MS )
}
// Stocks Aggregate:
{
"ev": "AM", // Event Type ( A = Second Agg, AM = Minute Agg )
"sym": "MSFT", // Symbol Ticker
"v": 10204, // Tick Volume
"av": 200304, // Accumulated Volume ( Today )
"op": 114.04, // Todays official opening price
"vw": 114.4040, // VWAP (Volume Weighted Average Price)
"o": 114.11, // Tick Open Price
"c": 114.14, // Tick Close Price
"h": 114.19, // Tick High Price
"l": 114.09, // Tick Low Price
"a": 114.1314, // Tick Average / VWAP Price
"s": 1536036818784, // Tick Start Timestamp ( Unix MS )
"e": 1536036818784, // Tick End Timestamp ( Unix MS )
}
以及可以包含上述任何类型的文本流:
[{"ev":"A","sym":"DAL","v":1,"av":1, ...snipped...},{"ev":"T","sym":"MSFT","p":114.11,"x":"4","s":67,"t":1586418423607, ...snipped... }]
并反序列化为:
class Message
{
List<Trade> Trades { get; set; }
List<Quote> Quotes { get; set; }
List<Aggregate> Aggs { get; set; }
}
我目前正在这样做,它有效,但怀疑与直接反序列化到 POCO 一样高效:
var array = JsonSerializer.Deserialize<dynamic>(@"[{""ev"":""A"",""sym"":""AAL"",""v"":1500,""av"":119037385,""op"":12.64,""vw"":13.1,""o"":13.1,""c"":13.1,""h"":13.1,""l"":13.1,""a"":12.6655,""z"":500,""s"":1586472438000,""e"":1586472439000},{""ev"":""A"",""sym"":""AAL"",""v"":6000,""av"":119043385,""op"":12.64,""vw"":13.1,""o"":13.1,""c"":13.1,""h"":13.1,""l"":13.1,""a"":12.6655,""z"":1000,""s"":1586472439000,""e"":1586472440000},{""ev"":""A"",""sym"":""AAL"",""v"":3000,""av"":119046385,""op"":12.64,""vw"":13.11,""o"":13.11,""c"":13.11,""h"":13.11,""l"":13.11,""a"":12.6655,""z"":1000,""s"":1586472440000,""e"":1586472441000}]");
foreach(var item in array)
{
if(item["ev"]=="A")
{
var aggregate = new OpenHighLowClose(
DateTimeOffset.FromUnixTimeMilliseconds((long)item["s"]),
(decimal)item["op"],
(decimal)item["h"],
(decimal)item["l"],
(decimal)item["c"],
(decimal)item["v"]);
}
else if(item["ev"=="AM"]) { }
else if(item["ev" == "T"]) { }
else if(item["ev" == "Q"]) { }
}
json.net的JsonConverter在Utf8Json中的等价物是什么,所以我可以根据EV 字段(以及 T、A、AM 或 Q 的关联字符串值)?
补充一下,我将从 JSON 转到 OpenHighLowClose POCO,然后我将通过消息包发送出去...有什么方法可以跳过这个中间步骤吗?
看起来像这样的消息怎么样:
class Message
{
List<IStockItem> Items { get; set; }
}
IStockItem 是一个接口,类 类型 Trade、Quote 和 Aggregate 继承自 IStockItem。
在您的自定义 JsonConverter 的 ReadJson 中,您将根据 ev
的值放置序列化条件public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
else if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize(reader, objectType);
}
else
{
JObject obj = JObject.Load(reader);
if (obj["ev"] != "A")
return // Logic for A
else if (obj["ev"] != "T")
return // Logic for T
else
return serializer.Deserialize(reader, objectType);
}
}
如果您可以假设 ev
字段在对象中始终排在第一位,则可以通过最少的分配和最少的额外解析来解决问题。 netcoreapp3.0以后,System.Text.Json
支持custom type converters,可以这样写:
[JsonConverter(typeof(MessageConverter))]
class Message
{
List<Trade> Trades { get; set; }
List<Quote> Quotes { get; set; }
List<Aggregate> Aggs { get; set; }
}
public class MessageConverter : JsonConverter<Message>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(Message).IsAssignableFrom(typeToConvert);
public override Message Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var message = new Message();
message.Trades = new List<Trade>();
message.Quotes = new List<Quotes>();
message.Aggs = new List<Aggs>();
while(Expect(ref reader, JsonTokenType.StartObject, JsonTokenType.EndArray))
{
Expect(ref reader, JsonTokenType.PropertyName);
var propertyName = reader.GetString();
if (propertyName != "ev")
throw new JsonException();
switch(ExpectString(ref reader))
{
default:
throw new JsonException();
case "T":
var trade = new Trade();
while(Expect(ref reader, JsonTokenType.PropertyName, JsonTokenType.EndObject))
switch(reader.GetString())
{
default: throw new JsonException();
case "sym": trade.Symbol = ExpectString (ref reader); break;
case "p": trade.Price = ExpectDecimal(ref reader); break;
...
}
message.Trades.Add(trade);
break;
...
}
}
return message;
}
public override void Write(Utf8JsonWriter writer, Message message, JsonSerializerOptions options) =>
throw new NotSupportedException();
private void Expect(ref Utf8JsonReader reader, JsonTokenType t)
{
reader.Read();
if (reader.TokenType != t)
throw new JsonException();
}
private string ExpectString(ref Utf8JsonReader reader)
{
Expect(ref reader, JsonTokenType.String);
return reader.GetString();
}
private decimal ExpectDecimal(ref Utf8JsonReader reader)
{
Expect(ref reader, JsonTokenType.Number);
return reader.GetDecimal();
}
private bool Expect(ref Utf8JsonReader reader, JsonTokenType a, JsonTokenType b)
{
reader.Read();
if (reader.TokenType == a) return true ;
if (reader.TokenType == b) return false ;
throw new JsonException();
}
}
如果由于某种原因您不能使用 netcoreapp3.x,那么您将不得不放弃 JsonSerializer
用于外部 Message
和 运行 Read
直接方法。如果您愿意花费一些周期来避免手动反序列化内部对象,请在您的 属性 名称
JsonPropertyName
属性
struct Trade
{
[JsonPropertyName("sym")]
public string Symbol { get; set; }
[JsonPropertyName("p")]
public decimal Price { get; set; }
...
}
并像这样重写 Read
方法的外层循环:
while (Expect(ref reader, JsonTokenType.StartObject, JsonTokenType.EndArray))
{
// save reader state to peek the object type
var copy = reader;
Expect(ref copy, JsonTokenType.PropertyName);
var propertyName = copy.GetString();
if (propertyName != "ev")
throw new JsonException();
switch(ExpectString(ref copy))
{
default:
throw new JsonException();
case "T":
message.Trades.Add(JsonSerializer.Deserialize<Trade>(ref reader));
break;
//...
}
}