如何使用可选属性反序列化JSON?

时间:2018-01-21 14:24:23

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

我正在尝试反序列化以下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个成员都是完全可选的(它们可能都在那里,或者它们都不是在那里)......这就是我做错了吗?如果是这样,我该如何修复它,并允许可选值。

更新:

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

enter image description here

1 个答案:

答案 0 :(得分:2)

制作UserContent条记录Option类型的成员,并为您的JSON序列化添加TypeConverter类型的Option,例如this one设置。

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)