【问题标题】:How do I deserialize JSON with optional properties?如何使用可选属性反序列化 JSON?
【发布时间】:2018-06-30 06:32:36
【问题描述】:

我正在尝试反序列化以下 JSON:

{
  "listings": {
    "-L19C5OjcDSjMi4-oha-": {
      "listing_id": "-L19C5OjcDSjMi4-oha-",
      "location": "Edinburgh"
    },
    "-L19CJrzEpChO_W14YkC": {
      "listing_id": "-L19CJrzEpChO_W14YkC",
      "location": "Edinburgh",
      "messages": {
        "Rp7ytJdEvZeMFgpLqeCSzkSeTyf1": {
          "-L19V4QpPMCMwGcNaQBG": {
            "senderId": "Rp7ytJdEvZeMFgpLqeCSzkSeTyf1",
            "senderName": "Albert",
            "text": "Hey there"
          },
          "-L19r0osoet4f9SjBGE7": {
            "senderId": "YMM45tgFFvYB7rx9PhC2TE5eW6D2",
            "senderName": "David",
            "text": "Hi"
          }
        }
      }
    },
    "-L19ChjPjX1DnfQb28AW": {
      "listing_id": "-L19ChjPjX1DnfQb28AW",
      "location": "Edinburgh",
      "messages": {
        "879dUqGuiXSd95QHzfhbSs05IZn2": {
          "-L1i6c7sGf3BcF2cCSCu": {
            "senderId": "879dUqGuiXSd95QHzfhbSs05IZn2",
            "senderName": "Alberto",
            "text": "Hello"
          }
        },
        "Rp7ytJdEvZeMFgpLqeCSzkSeTyf1": {
          "-L19FGCMuQACjYKCFEwV": {
            "senderId": "Rp7ytJdEvZeMFgpLqeCSzkSeTyf1",
            "senderName": "Albert",
            "text": "Hey"
          },
          "-L19T_v2Utxhu1mGhz7-": {
            "senderId": "YMM45tgFFvYB7rx9PhC2TE5eW6D2",
            "senderName": "David",
            "text": "Hi"
          },
          "-L19TbhActGmga4f47Mz": {
            "senderId": "Rp7ytJdEvZeMFgpLqeCSzkSeTyf1",
            "senderName": "Albert",
            "text": "How are you"
          }
        }
      }
    },
    "-L19Cz1abm1o-JCbiAnN": {
      "listing_id": "-L19Cz1abm1o-JCbiAnN",
      "location": "Edinburgh"
    },
    "-L19DMdFx2pXj9-EKCq2": {
      "listing_id": "-L19DMdFx2pXj9-EKCq2",
      "location": "Edinburgh"
    },
    "-L19DV67WjguozFE_4dM": {
      "listing_id": "-L19DV67WjguozFE_4dM",
      "location": "Edinburgh"
    }
  }
}

为此,我创建了以下记录:

type MessageContent = 
    { senderId: string
      senderName: string
      text: string; }

type Message =
     { timestampId : string
       chatMessages : MessageContent;}

type Chat = 
    { chatPartnerId : string 
      Messages : Message array;}

type ListingContent = 
    { from : string
      landlord_id : string
      listing_id : string
      location : string
      name : string
      pic_1_url : string
      pic_2_url : string
      pic_3_url : string
      pic_4_url : string
      pic_5_url : string
      messages : Chat array
      postcode : string
      price_per_night : int
      to_date : string;
    }

type Listing =
     { timestampId : string
       chatMessages : ListingContent;}

type City = 
    { city : string
      listings : Listing array
    }

type AllListings = 
    { cities : City array;}

type SearchSettings = 
    { from : string
      location : string
      max_price : decimal
      min_price : decimal
      to_date : string;}

type MatchContent = 
    { id : string
      location : string;}

type Match = 
    {timestampId : string
     matchContent : MatchContent;}

type DeclinedContent = 
    { id : string;
      }

type Declined = 
    {timestampId : string
     declinedContent : DeclinedContent;}

type ListingUserContent = 
    { listing_id : string
      location : string
      messages : Chat array;
    }

type ListingUser = 
    {timestampId : string
     listingUser : ListingUserContent;}

type UserContent = 
    { declined: Declined array
      matches : Match array
      searchSettings : SearchSettings
      user_listings : ListingUser array;
    }

接下来,我有以下代码行:

let listings = JsonConvert.DeserializeObject<Types.UserContent>(html) 

其中html 是上面显示的 JSON 字符串。

但是,这会引发以下错误:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'Types+Declined[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'declined.-L0tmKVgUcj_a1ubO5Zd', line 1, position 36.

我相信这可能是因为在这个特定的 JSON 中没有 Declined,但是 UserContent 记录的所有 4 个成员都是完全可选的(它们可能都在那里,或者它们都不在那里)。 ..这是我做错了吗?如果是这样,我该如何解决它,并允许可选值。

更新:

所以我注释掉了实际执行反序列化的代码,但我仍然收到奇怪的错误,我认为它与我的代码无关

【问题讨论】:

    标签: json serialization f# xamarin.ios json.net


    【解决方案1】:

    UserContent 的成员记录为Option 类型,并将TypeConverter 用于F# Option 类型,例如this one,添加到您的JSON 序列化设置中。

    type UserContent = 
        { declined: Declined array option
          matches : Match array option
          searchSettings : SearchSettings option
          user_listings : ListingUser array option;
        }
    
    type OptionConverter() =
        inherit JsonConverter()
    
        override __.CanConvert(t) = 
            t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
    
        override __.WriteJson(writer, value, serializer) =
            let value = 
                if value |> isNull 
                then null
                else let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
                     fields.[0]  
            serializer.Serialize(writer, value)
    
        override __.ReadJson(reader, t, existingValue, serializer) =        
            let innerType = t.GetGenericArguments().[0]
            let innerType = 
                if innerType.IsValueType 
                then (typedefof<Nullable<_>>).MakeGenericType([|innerType|])
                else innerType        
            let value = serializer.Deserialize(reader, innerType)
            let cases = FSharpType.GetUnionCases(t)
            if value |> isNull 
            then FSharpValue.MakeUnion(cases.[0], [||])
            else FSharpValue.MakeUnion(cases.[1], [|value|]) 
    
    let serializer = JsonSerializer.Create(JsonSerializerSettings(Converters = [| OptionConverter() |]))
    
    use stringReader = new StringReader(html)
    use jsonReader = new JsonTextReader(stringReader)
    serializer.Deserialize<Types.UserContent>(jsonReader)
    

    【讨论】:

    • 它现在抛出这个错误,Expected JSON property 'Case'
    • 能否请您提供我必须使用的代码,而不是JsonConvert.DeserializeObject&lt;Types.UserContent&gt;(html)... 我有一个streamReader,可以将整个JSON 转换成一个名为html 的字符串
    • 嗯,您的记录类型与 JSON 文档不匹配存在更多问题。我的回答解决了可选字段的问题,但没有解决您的 F# 记录类型与您尝试反序列化的 JSON 之间的任何不匹配问题。实际上在我看来ListingUserContent 需要有一个可选的messages 字段,并且JSON 中listings 节点中的元素比UserContent 类型更多地映射到该类型。
    • 我已将请求的代码添加到示例的末尾。您只需要从您的 json 内容的StringReader 创建一个JsonTextReader,然后调用serializer.Deserialize&lt;'record&gt;(jsonReader)
    • 我查看了 Newtonsoft.Json 源代码,在运行 ReadJson 时在 EntityKeyMemberConverter 中引发了该异常。您确定您还没有在某处使用JsonConvert.DeserializeObject 吗?我还查看了JsonTextReader 的构造函数,以确保它不能抛出,我认为它不能。尝试对您的解决方案进行清理/重建。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 2014-05-16
    • 1970-01-01
    • 2016-07-07
    相关资源
    最近更新 更多