C# Utf8Json反序列化为基于标记字段的类型

C# Utf8Json反序列化为基于标记字段的类型,c#,utf8json,C#,Utf8json,通过使用Json.NET-Newtonsoft我成功地使用了自定义契约反序列化程序和Json转换器,根据标记选择反序列化程序(在下面的ev案例中) 总之,我希望通过Utf8Json实现同样的效果,详细信息如下: // Stocks TRADE: { "ev": "T", // Event Type "sym": "MSFT", // Symbol Ticker "x": "4", // Exchang

通过使用
Json.NET-Newtonsoft
我成功地使用了自定义契约反序列化程序和Json转换器,根据标记选择反序列化程序(在下面的
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; }
}
类消息
{
列出交易{get;set;}
列表引号{get;set;}
列表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"]) { }
}
var array=JsonSerializer.反序列化(@“{”ev“:”A“,”sym“:”AAL“,”v“:1500”,“av“:119037385”,“op”“:12.64”,“vw”“:13.1”,“c”“:13.1”,“h”“:13.1”,“l”“:13.1”,“A”“:12.6655”,“z”“:500”,“s”“:158642438000”,“e”“:158642439000},{”ev“:”A“,”sym“:”AAL“,”v”“:13.1”,“vw”“:13”“,”vw“:13.1”“,”vw”“:1”“,”vw“,”s”“,”s”“,”s”“,”s”“:158642438000”“,”e”“,”e”“,”e”“,”A”“,”s”“,”s”“,”s”“,”s”“,”s”“,”s”““:13.1”“l”“:13.1”“a”“:12.6655”“z”“:1000”“s”“:158647249000”“e”“:158647240000},{”“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:1586440000”“e”“,”“1586440000”“”;
foreach(数组中的变量项)
{
如果(项目[“ev”]=“A”)
{
var聚合=新的OpenHighLowClose(
DateTimeOffset.FromUnixtimeMissions((长)项[“s]”),
(十进制)项目[“op”],
(十进制)项目[“h”],
(十进制)项目[“l”],
(十进制)项目[“c”],
(十进制)项目[“v”];
}
如果(项目[“ev”=“AM”]){}
如果(项目[“ev”=“T”]){}
如果(项目[“ev”=“Q”]){}
}
Utf8Json中,json.net的JsonConverter的等价物是什么,这样我就可以根据EV字段(以及T、A、AM或Q的相关字符串值)切换反序列化器了


补充一下,我将从JSON转到并希望打开HighLowClose POCO,然后通过消息包发送出去……有没有办法跳过这个中间步骤?

如果消息看起来更像这样:

class Message
{
   List<IStockItem> Items { get; set; }
}
[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();
    }
}
        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;
            //...
            }
        }

如果您可以假设
ev
字段始终位于对象的第一位,则可以通过最少的分配和最少的额外解析来实现。在netcoreapp3.0及更高版本中,
System.Text.Json
支持,可以编写如下代码:

class Message
{
   List<IStockItem> Items { get; set; }
}
[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();
    }
}
        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;
            //...
            }
        }
然后像这样重写
Read
方法的外循环:

class Message
{
   List<IStockItem> Items { get; set; }
}
[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();
    }
}
        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;
            //...
            }
        }
while(预期(ref-reader、JsonTokenType.StartObject、JsonTokenType.EndArray))
{
//保存读取器状态以查看对象类型
var copy=读取器;
Expect(ref copy,JsonTokenType.PropertyName);
var propertyName=copy.GetString();
如果(propertyName!=“ev”)
抛出新的JsonException();
开关(预期字符串(参考副本))
{
违约:
抛出新的JsonException();
案例“T”:
message.Trades.Add(JsonSerializer.Deserialize(ref-reader));
打破
//...
}
}

感谢Ctznkane525花时间回复,我相信上面的代码是为Json.net编写的-谢谢,我同意,谢谢,在UTF8JS中寻找等效代码。您能保证
ev
字段在记录中总是排在第一位吗?是的,可以假设,我没有看到任何没有的数据数以百万计的记录。当然可以跳过中间步骤,但这需要手动处理从Utf8JsonReader接收到的JSON令牌,并手动创建MessagePack字节流。JSON很好地映射到MessagePack上,因此代码应该很简单。如果您愿意,我建议您提出另一个问题无法解决此问题。请查看此问题:。创建自定义IJsonFormatter并或多或少实现@AntonTykhyy在其答案中描述的内容。请记住使用[JsonFormatter(typeof(MessageFormatter))]装饰您的
消息
类或者注册此格式化程序。感谢Anton,对于数组,我该如何处理?我看到了JsonTokenType.StartArray,如何循环到最后(因为元素是动态的)?您具体指的是哪些数组?在
交易
类型下,我们有一个条件数组
“c”:[0,12]
您可以手动编写反序列化代码,从
Expect(ref reader,JsonTokenType.StartArray)开始
并循环,直到获得
EndArray
标记,但是如果您没有压缩最后的性能百分比,我真的建议对
Trade
s和其他内部对象使用本机序列化程序,就像在我的上一个代码段中一样。只需添加
[JsonPropertyName(“c”)]public int[]条件{get;set;}
交易
就可以了。