fsprojects / FSharp.Json

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

Is JSON in JSON possible #26

Closed vilinski closed 5 years ago

vilinski commented 5 years ago

Hi is it possible with this serializer to deserialize some json subdocument to a string as is? Just like JObject in Json.NET? I need it to encode a http request content. Otherwise it should be escaped, what by manually filled files is not comfortable

vsapronov commented 5 years ago

Hi, could you please check AsJson option of JsonField, tests could be found here: https://github.com/vsapronov/FSharp.Json/blob/master/FSharp.Json.Tests/AsJson.fs#L8 it should give you an idea about how it works. It looks like it's what you described. Otherwise, please provide code example so that I can see what exactly you want to get.

vsapronov commented 5 years ago

You can also take a look at untyped data feature: https://github.com/vsapronov/FSharp.Json#untyped-data. Please let me know if that still does not work for you.

vilinski commented 5 years ago

Thanks! AsJson seems to be what I need.

I need to deserialize a requests.json into this.

The last thing which might be nice to have is ignoring empty collections. My understanding, if I deserialize Map<,> it should be set to {} in json or be a Map<,> option on the F# side? And the same for list.

vsapronov commented 5 years ago

Hi, I reiterated all open issues here. Do you still have any problems?

After you provided samples I would say you do not need any special features to parse your JSON. The AsJson feature is needed when you want some JSON to be represented as a string in F# code so that you do not need to care about structure in that JSON. It's often needed for some nested envelope JSONs.

Your case is different since the body is really a string (not JSON) in your JSON payload:

{
  "url" : "/api/ticket/",
  "method": "POST",
  "body": "{\"id\":\"1\",\"title\":\"Test ticket 1\"}"
}

See those quoutes " around body value? It means body is just a string in your JSON. To parse this you can use simple record type:

type Request = {
  url: string,
  method: string option,
// No AsJson is needed here because body is just a string in JSON
  body: string option
}

Then if your body has particular structure you can process request additionally and parse body of string option into some Body option:

type Body = {
  id: string,
  title: string
}

Such Body type could be parsed with FSharp.Json library. It required additional step.

There's no feature that will allow you to magically parse body in one shot, there's no feature like this:

type Request = {
  url: string,
  method: string option,
  [JsonField(AsString=true)] // there's no such setting AsString that would put JSON into string value
  body: Body option
}

Though it could be good feature request.

vsapronov commented 5 years ago

Also you are trying to create types from System.Net.Http like HttpContent. That is class from .Net standard library. FSharp.Json does not work with classes. So you will need to create record types if you want to use FSharp.Json to parse your JSON.

vilinski commented 5 years ago

Thank you for your explanations. As I already mentioned AsString attribute was exactly what I actually needed. The body was as such just because I had to escape it, which was not my intent. I do not even know or care about content, other than it should be a valid json.

Still guessing whether is it possible to avoid option everywhere. For example on lists. Could be more interesting to deserialize missing collection property to an empty collection. In this case it is needed to deserialize http headers without forcing user to create an empty subdocument.

Short said I miss a default value handling like in boring Newtonsoft. But I guess it's by design or intentionally not possible.

So you can close the issue, which was rather a question anyway :)

vsapronov commented 5 years ago

There's a DefaultValue feature in FSharp.Json as well: https://github.com/vsapronov/FSharp.Json/blob/master/FSharp.Json.Tests/Default.fs#L8

Though, the problem is that default value is set through attribute. And attribute's fields can be set only to constant expression. Empty list is not a constant expression. By the way the same issue you would have with Newtonsoft: empty list just can't be set as a default value.

Overall using attributes becoming less OK lately. Attributes are not supported in Fable. I think I will drop attributes in future and provide some other way of customizing F# to JSON mappings. Just dreaming...