Reputation: 5557
I'm trying to convert the following Firebase JSON into something that can be parsed in F#
:
{
"listings":{
"-L0pJmU9yj4hAocHjnrB":{
"listing_id":"-L0pJmU9yj4hAocHjnrB",
"location":"Edinburgh",
"messages":{
"SWs56OIGzMdiCjSXahzDQX8zve92":{
"-L3ELSSzZPRdjCRcFTrb":{
"senderId":"SWs56OIGzMdiCjSXahzDQX8zve92",
"senderName":"alberto",
"text":"Hi"
},
"-L3EN1NW5hHWBTEGC9ve":{
"senderId":"YMM45tgFFvYB7rx9PhC2TE5eW6D2",
"senderName":"David",
"text":"Hey"
}
}
}
},
"-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"
},
"-L3ELGAbcOjdJsHRtnAe":{
"senderId":"YMM45tgFFvYB7rx9PhC2TE5eW6D2",
"senderName":"David",
"text":"Icvjv"
}
}
}
},
"-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"
}
}
}
The problem here is that the entries like L0pJmU9yj4hAocHjnrB
in the 2nd line and subsequent similar entries are autogenerated timestamp IDs created in Firebase, and they do not have a corresponding name, like for example: "listing_id":"-L0pJmU9yj4hAocHjnrB"
, therefore I do not know how to set up my F#
Records to correctly parse this JSON.
My attempt can be seen below:
type MessageContent =
{ senderId: string
senderName: string
text: string; }
type Message =
{ timestampId : string
chatMessages : MessageContent;}
type Chat =
{ chatPartnerId : string
Messages : Message array option;}
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 option
postcode : string
price_per_night : int
to_date : string;
}
type Listing =
{ timestampId : string
listingcontent : ListingContent option;}
type City =
{ city : string
listings : Listing array option
}
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 option;
}
type ListingUser =
{timestampId : string
listingUser : ListingUserContent;}
type UserContent =
{ declined: Declined option
matches : Match option
search_settings : SearchSettings option
listings : ListingUser option;
}
I use the following code for parsing:
let myCallbackGetChats (reader:IO.StreamReader) url =
let html = reader.ReadToEnd()
let reader = new JsonTextReader(reader);
let serializer = JsonSerializer.Create(JsonSerializerSettings(Converters = [| Types.OptionConverter() |]))
use stringReader = new StringReader(html)
use jsonReader = new JsonTextReader(stringReader)
let listings_json = serializer.Deserialize<Types.UserContent>(jsonReader)
printfn "%A" listings_json
This produces the following output:
{declined = null;
matches = null;
search_settings = null;
listings = Some {listing_id = null;
location = null;
messages = null;};}
As we can see the first listings
tag is properly deserialized, however as soon as it sees L0pJmU9yj4hAocHjnrB
it doesn't know what that is and the rest of the parsing fails. How can I fix this issue?
Upvotes: 0
Views: 100
Reputation: 117076
You can deserialize your JSON with the following F# types:
type MessageContent =
{ senderId: string
senderName: string
text: string;
}
type ListingContent =
{ listing_id : string
location : string
messages : Map<string, Map<string, MessageContent>>
// Add other members as required
}
type UserContent =
{ listings: Map<string, ListingContent>
// Add other members as required
}
And the following line of code:
let listings_json = JsonConvert.DeserializeObject<UserContent>(inputJson)
Notes:
The "listing"
object consists of variable property names with a fixed schema for their values. As explained in Serialization Guide: Dictionaries and Hashtables such objects can be mapped to a .Net dictionary. In this case I chose the F# Collections.Map<'Key,'Value>
class, specifically a Map<string, ListingContent>
.
Similarly, the "messages"
object consists of variable property names whose values are a nested level of objects with variable property names, and so can be represented with a Map<string, Map<string, MessageContent>>
.
I simplified the UserContent
and ListingContent
types by removing members not actually present in the sample JSON. You can add them back as required.
Since Json.NET has built-in support for dictionaries, a custom JsonConverter
is not required for this solution.
Example working F# fiddle.
Upvotes: 2