首页 > 解决方案 > F# 中的 json.net 反序列化问题

问题描述

我有一个要反序列化的简单对象,但我不明白我得到的错误。

代码如下:

open System
open Newtonsoft.Json

type r =
    {
        Timestamp:           DateTime
        Currency:            string

        PreviousDeposited:   int64 option
        PreviousWithdrawn:   int64 option
        PreviousTransferIn:  int64 option
        PreviousTransferOut: int64 option
        PreviousAmount:      int64 option

        TransferIn:          int64 option
        TransferOut:         int64 option

        Amount:              int64 option

        PendingCredit:       int64 option
        PendingDebit:        int64 option
        ConfirmedDebit:      int64 option
    }


let a =
    "{
    \"account\": 117122,
    \"currency\": \"XBt\",
    \"prevDeposited\": 747841316,
    \"prevWithdrawn\": 2160000,
    \"prevTransferIn\": 1000000,
    \"prevTransferOut\": 0,
    \"prevAmount\": 656893723,
    \"prevTimestamp\": \"2020-06-13T12:00:00.005Z\",
    \"deltaDeposited\": 0,
    \"deltaWithdrawn\": 0,
    \"deltaTransferIn\": 0,
    \"deltaTransferOut\": 0,
    \"deltaAmount\": 0,
    \"deposited\": 747841316,
    \"withdrawn\": 2160000,
    \"transferIn\": 1000000,
    \"transferOut\": 0,
    \"amount\": 656893723,
    \"pendingCredit\": 0,
    \"pendingDebit\": 0,
    \"confirmedDebit\": 0,
    \"timestamp\": \"2020-06-13T12:00:00.643Z\",
    \"addr\": \"2NBMEXRW4oCiNzVUq4uVFRSsK2jtTLbtfc7\",
    \"script\": \"532102c10be2f0dc20f4285c25156aa22a0c46d2b89ccc4d1c8eaed92ea0c1a8f40c002102ceba29da1af96a0f2ef7cda6950b8be2baeb1adf12c0d5efebb70dbcaa086ba021034ab762f4ede40311e9f8bf01db0bbea578497ac6ccc8aa94a74394b05a53d94b2103d5a42b90e9d7156155661979530a09d2e12e252ef4104e5611274a7ae7e2b09454ae\",
    \"withdrawalLock\": []
    }"


JsonConvert.DeserializeObject<r> a

我得到这个错误:

Newtonsoft.Json.JsonSerializationException:读取联合时发现意外的属性“transferOut”。路径 'transferOut',第 18 行,位置 18。] Newtonsoft.Json.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter 转换器,JsonReader Reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndCreatorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType) at ...

我不明白是什么让“TransferOut”属性如此特别,以至于它停在这个上,而不是之前的任何其他相同的上。

我在这里有一个小提琴:https ://dotnetfiddle.net/HGiia5

标签: f#json.net

解决方案


你在这里有几个问题。

首先,您用于option字段的 JSON 语法与 Json.NET 的语法不匹配。如果我们将您的类型简化如下:

type r =
    {
        TransferIn:          int64 option
        TransferOut:         int64 option
    }

并序列化一个实例如下:

let item : r = { TransferIn = Some 1000000L; TransferOut = None}
let json = JsonConvert.SerializeObject(item,Formatting.Indented)
printfn "%s" json
let item2 = JsonConvert.DeserializeObject<r> json // No exception

结果是:

{
  "TransferIn": {
    "Case": "Some",
    "Fields": [
      1000000
    ]
  },
  "TransferOut": null
}

哪个往返成功。演示小提琴#1在这里

"transferIn": 1000000您用于option字段的简单语法不是由DiscriminatedUnionConverterJson.NET 用于序列化包括可选字段的可区分联合的转换器实现的。这种不匹配导致读取 JSON 时出现异常。

相关地,请参阅序列化 F# 选项类型,其中建议使用 nuget 包,该包提供支持此简化语法的JsonConverterfor 。option<_>

其次,许多 JSON 属性名称与您的 f# 记录名称不匹配。Json.NET 使用不区分大小写的算法将 JSON 属性名称与 f# 构造函数参数和成员名称匹配,但您的许多 JSON 名称不匹配:

  • "prevDeposited"不匹配PreviousDeposited
  • "prevWithdrawn"不匹配PreviousWithdrawn
  • 还有其他几个。

事实上,JSON 中第一个真正匹配option字段的属性是"transferIn". 您收到一个错误,"transferOut"因为它紧跟"transferIn"未成功反序列化的值。

最后,当字段没有出现在 JSON 对象的末尾时,Json.NET 为option字段的无效 JSON 值抛出的错误消息是无用的。如果我将输入 JSON 简化如下:

{
    "transferIn": 1000000,
}

我们得到一个更有用的错误信息

Newtonsoft.Json.JsonSerializationException:找不到具有联合名称的“案例”属性。路径'',第 3 行,位置 1。

演示小提琴#2在这里

但是,当"transferIn"后面跟着另一个 JSON 键/值对时,错误消息会变成您的问题中显示的不太有用的消息。您可能会向Newtonsoft 提出问题DiscriminatedUnionConverter,要求他们改进当字段的 JSON 值option与预期架构不匹配并且包含对象中存在后续 JSON 属性时引发的错误消息。


推荐阅读