fsprojects / FSharp.Json

F# JSON Reflection based serialization library
Apache License 2.0
226 stars 26 forks source link

Is it possible to parse json with mixed lists? #11

Closed NicoVIII closed 2 years ago

NicoVIII commented 6 years ago

At first: Thanks for this library! I'm working with it to parse JSON responses from an API and it really works like a charm. If I correctly create types for the expected response, I can work type-safe with that data. But now the API sends inconsistent data, I get a list where complex objects and strings are mixed.

It looks like that: { "downloads": [ [ "English", {..} ] ] }

Is there some elegant way to handle such mixed lists with this library? I tried different approaches but none worked.

vsapronov commented 6 years ago

The one simplest option would be to try to deserialize as object list type. Unfortunately not really helpful as you will need to do types casting in F# code manually.

The other more helpful option would be: serialize/deserialize record type into list with members mapped positionally. Meaning that #0 element of the list always goes to particular member of the record type, #1 element goes to another member of the record type, etc. Mappings #0 #1 to record members could be done through attribute as names are mapped now. Is this what you would like to have? For this to work your lists should be always of the same structure. As you can imagine this is big work but not impossible, so let's agree on what you need first....

vsapronov commented 6 years ago

@NicoVIII ^

NicoVIII commented 6 years ago

Thank you for your fast response and sorry for my late one :grin:

I guess, the list will always have the same structure so your second approach should be usable. I'm a bit confused though, is it a suggestion for me to implement in my program or a possible new feature for the library itself?

I tried the "obj list" approach. It is not beautiful, but works for now. So no need to hurry.

sebastian commented 5 years ago

@vsapronov: thanks for the library from my side too!

@vsapronov, @NicoVIII: would you happen to have sample code for how to work with parsed obj list? Say the input data is an array of unpredictable length with different types. For example like: [1, "foo", true, "bar"].

Ideally I would like it to end up like something like:

type DecodedValue = 
  | String of string
  | Integer of int
  | Bool of bool

type ListType = DecodedValue list

// desired parsed outcome
let decodedValues : ListType = [Integer 1; String "foo"; Bool true; String "bar"]

Maybe this is obvious to someone more seasoned in F#. I would appreciate any pointers if you have them available!

Thanks

NicoVIII commented 4 years ago

Hi! I'm very sorry that I didn't answer in time and am quite sure, that my answer now will not help you since it took me over a year...

I personally used Transforms to read out my information in a helpful way for me. You can have a look at that here: https://github.com/NicoVIII/GogApi.DotNet/commit/7a89344ca8da7164d7f4f0190031fd9887957c99

I'm not very experienced in F# (I only use it in hobby projects) so I'm not sure if this is the best way. But I guess a similar approach could work to get a list like you requested there:

Write a Transform from DecodedValue list into a obj list and write transformation functions for both directions (or only the one you need, I used only one direction, because I'm only reading) and add it to the field with [<JsonField(Transform=typeof<...>)>].

To transform the types I cast it one by one to the assumed type (for me the type at pos 1 was always the same). I'm not sure how to get the correct type here for the DecodedValue DU, but I guess you could use a type match approach?

// Untested - call it Pseudocode
match something with
| :? string as value -> String value
| :? int as value -> Integer value
...

(https://fsharpforfunandprofit.com/posts/match-expression/#matching-on-subtypes)

Maybe this helps when people in the future need something like that again...

sebastian commented 4 years ago

Thank you @NicoVIII! I really appreciate your detailed answer and the links! A year or not, I still learnt lots reading this!