将 JSON 键不准确地匹配到 class 字段

Matching JSON keys to class fields inexactly

我正在尝试将 JSON 流反序列化为 VB.NET 对象。但是,每当我尝试将值分配给对象中的字段时,其中一个字段不会编译,因为 JSON 字段包含一个连字符,据我所知,我需要匹配我的字段名称和 JSON 键名完全一样。这是 JSON:

的示例
{
    "email":"john.doe@sendgrid.com",
    "sg_event_id":"VzcPxPv7SdWvUugt-xKymw",
    "sg_message_id":"142d9f3f351.7618.254f56.filter-147.22649.52A663508.0",
    "timestamp":1386636112,
    "smtp-id":"<142d9f3f351.7618.254f56@sendgrid.com>",
    "event":"processed",
    "category":["category1","category2","category3"],
    "id":"001","purchase":"PO1452297845","uid":"123456"
}

此 JSON 示例来自测试流(通过 POST)到旨在与 SendGrid 的事件 Webhook 进行通信的 Webhook API (https://sendgrid.com/docs/API_Reference/Webhooks/event.html)

我有一个 class 分配给了 JSON,如下所示:

Partial Class SendGridEvent
    Public email As String
    Public sg_event_id As String
    Public sg_message_id As String
    Public timestamp As String
    Public smtp-id As String
    Public [event] As String
    Public category() As String
    Public id As String
    Public purchase As String
    Public uid As String
    Public reason As String
    Public ip As String
    Public useragent As String
    Public url As String
    Public status As String
    Public type As String
End Class

我已经成功地让除了一个 (smtp-id) 之外的所有字段在流通过时正确分配。这样就完成了(忽略列表声明,SendGrid是批量发送邮件信息的,但我担心它会把例子弄得更乱):

' Create new stream reader to take in serialized JSON from SendGrid
Dim oSR = New StreamReader(Request.InputStream)
' Store entire stream in new String variable called sContent
Dim sContent As String = oSR.ReadToEnd()

' Declare a new list of SendGridEvent objects
Dim masterOutput = New List(Of SendGridEvent)
' Declare new SendGridEvent objects within the list called masterOutput
Dim a = New SendGridEvent
Dim b = New SendGridEvent
Dim c = New SendGridEvent
Dim d = New SendGridEvent

' Assign individual SendGridEvent object just created to the list of SendGridEvents
masterOutput.Add(a)
masterOutput.Add(b)
masterOutput.Add(c)
masterOutput.Add(d)
' Assign the deserialized JSON String to the BulkEmail array named output, via BulkEmail array deserialization
Dim res = JsonConvert.DeserializeObject(Of List(Of SendGridEvent))(sContent)

' Declare String to use in For Each loop
Dim sContentPair As String = ""

' Write the info from each SendGridEvent in the master SendGridEvent list (masterOutput) to the variable sContentPair
For Each data As SendGridEvent In res
    sContentPair = sContentPair + " => " + data.email + " => " + data.sg_event_id + " => " + data.sg_message_id + _
        " => " + data.timestamp + " => " + data.smtp-id + " => " + data.event + " => " + data.category(0) + " => " + data.id _
    + " => " + data.purchase + " => " + data.uid + " => " + data.reason + " => " + data.ip + " => " + data.useragent _
    + " => " + data.url + " => " + data.status + " => " + data.status + " => " + data.type + " STOP "
Next

使用上面的 JSON 示例流,输出是这样的(我为不整洁的格式道歉):

=> john.doe@sendgrid.com => VzcPxPv7SdWvUugt-xKymw => 
142d9f3f351.7618.254f56.filter-147.22649.52A663508.0 => 1386636112 => 
=> processed => category1 => 001 => PO1452297845 => 123456 => => => 
=> => => => STOP

SendGridEvent class 中的 属性 smtp-id 不会编译,因为连字符结束了标识符声明。因此,JSON 流中的 smtp-id 字段无法将自身分配给对象中的 属性。我曾尝试使用字段名称 smtp_002D_id 来强制在字段名称中使用连字符(因此我设法获得了示例输出,否则程序无法编译)但是正如您所见,随后没有分配任何值。

我浏览了 50 多个网页和几本书来找到解决这个普遍问题的方法。除了你在这里看到的,我对 serialization/deserialization 并不熟悉,而且我还没有找到一个很好的参考来说明如何将值分配给 class 属性(是否 属性 名称需要精确匹配 JSON 键名,如果它们需要按照流吐出的确切顺序等)。

下面的答案

感谢 Tim 和 Juan,解决方案相当快。将 JsonProperty 属性附加到字段允许 JSON 流正确分配给正确的属性。更正以下代码:

Partial Class SendGridEvent
    <JsonProperty("email")>
    Public Email As String
    <JsonProperty("sg_event_id")>
    Public SgEventId As String
    <JsonProperty("sg_message_id")>
    Public SgMessageId As String
    <JsonProperty("timestamp")>
    Public Timestamp As String
    <JsonProperty("smtp-id")>
    Public SmtpId As String
    <JsonProperty("event")>
    Public EmailEvent As String
    <JsonProperty("category")>
    Public Category() As String
    <JsonProperty("id")>
    Public Id As String
    <JsonProperty("purchase")>
    Public Purchase As String
    <JsonProperty("uid")>
    Public UId As String
    <JsonProperty("reason")>
    Public Reason As String
    <JsonProperty("ip")>
    Public Ip As String
    <JsonProperty("useragent")>
    Public UserAgent As String
    <JsonProperty("url")>
    Public Url As String
    <JsonProperty("status")>
    Public Status As String
    <JsonProperty("type")>
    Public Type As String
End Class

这个问题我遇到过一次,只是把smtpid替换成smtpid,然后序列化。

将 class 属性 更改为 smtpid,一切正常。

根据 Tim 的回答,我想出了这个 class 并进行了测试并且有效。您需要使用 Newtonsoft.Json;

添加
     public class DataClass
            {
                public string email { get; set; }
                public string sg_event_id { get; set; }
                public string sg_message_id { get; set; }
                public int timestamp { get; set; }

                 [JsonProperty("smtp-id")]
                public string smtp { get; set; }
                 [JsonProperty("event")]
                   public string eve { get; set; }
                public List<string> category { get; set; }
                public string id { get; set; }
                public string purchase { get; set; }
                public string uid { get; set; }
            }



This code is from a console app test:


               string json = "{";
    json += "\"email\":\"john.doe@sendgrid.com\"" + ",";
    json +="\"sg_event_id\":\"VzcPxPv7SdWvUugt-xKymw\"" + "," ;
    json += "\"sg_message_id\":\"142d9f3f351.7618.254f56.filter-147.22649.52A663508.0\"" + ",";
   json += "\"timestamp\":1386636112" + ",";
   json += "\"smtp-id\":\"<142d9f3f351.7618.254f56@sendgrid.com>\"" + ",";
   json += "\"event\":\"processed\"" + ",";
   json += "\"category\":[\"category1\",\"category2\",\"category3\"]" + ",";
   json += "\"id\":\"001\",\"purchase\":\"PO1452297845\",\"uid\":\"123456\"" ;
   json +="}";

            DataClass data = JsonConvert.DeserializeObject<DataClass>(json);

            Console.Read();

您应该使用 JsonProperty 属性让 Json.NET 知道 smtp-id 映射到您的字段。

Partial Class SendGridEvent
    Public email As String
    Public sg_event_id As String
    Public sg_message_id As String
    Public timestamp As String
    <JsonProperty("smtp-id")>
    Public smtp_id As String
    Public [event] As String
    ...

如果它有助于您的代码更清晰,您可能还想将其用于其他字段。例如。您可能应该为 event 字段选择一个非关键字名称,并且您可能希望遵循 PascalCase 的 .NET 标准而不是 JSON 样式 under_scores(例如 SgEventId 而不是 sg_event_id).

此外,那些应该(几乎可以肯定)都是属性,而不是字段。