Coder Social home page Coder Social logo

thoth.json.net's Introduction

Thoth.Json.Net

Library for working with JSON in a type safe manner, this libs is the .NET version of Thoth.Json.

Learn more about Thoth.Json here.

thoth.json.net's People

Contributors

alfonsogarciacaro avatar bentayloruk avatar mangelmaxime avatar rommsen avatar samuelberger avatar scullman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

thoth.json.net's Issues

Decode.keyValuePairs errors lack specific location

How to reproduce:

#r "nuget:Thoth.Json.Net"
open Thoth.Json.Net

let decoder = Decode.keyValuePairs Decode.int
Decode.unsafeFromString decoder """ { "a": 1, "b": 2, "c": false } """
System.Exception : Error at: `$`
Expecting an int but instead got: false
   at Thoth.Json.Net.Decode.unsafeFromString[T](FSharpFunc`2 decoder, String value)

The problem is, the error does not show the problem occured when parsing "c" property of an object.

Transient dependencies and upgrade from 3.5.1 to 3.6.0

Projects that upgrades from 3.5.1 to 3.6.0 and depends on NuGet's built with 3.5.1 breaks with runtime error: "Method not found: 'Microsoft.FSharp.Core.FSharpResult2<Int32,System.Tuple2<System.String,Thoth.Json.Net.ErrorReason>> Thoth.Json.Net.Decode.int(System.String, Newtonsoft.Json.Linq.JToken)'."

Just a heads up. I have no idea what the problem is. Recompiling dependencies with 3.6.0 seems to fix the problem.

Problems with FSharp.Core dependency

Not sure if it's related with the problems you had in SAFE-Stack/SAFE-BookStore#388 but there seems to be an issue with the FSharp.Core dependency in the Thoth.Json.Net package. This is causing an issue in one of my projects when trying to use Thoth.Json.Net 3.0.0-beta-005.

In Nuget, the version of the FSharp.Core dependency is 0.0.0:

image

@forki do you have any idea why this happens? Maybe it's related with the multiple target frameworks?

Handling of Enums

Currently encoding and decoding of ordinary enums like System.DayOfTheWeek require own extra coders, e.g.

module Encode =
   let dayofWeek (value: System.DayOfWeek) : JsonValue =
       LanguagePrimitives.EnumToValue (value)
       |>JValue :> JsonValue


module Decode =
    module Helpers =
        let inline isInteger (token: JsonValue) = not(isNull token) && (token.Type = JTokenType.Integer)
        let inline isString (token: JsonValue) = not(isNull token) && token.Type = JTokenType.String
        let inline asString (token: JsonValue): string = token.Value<string>()
    
    let dayofWeek : Decoder<System.DayOfWeek> =
        fun path token ->
            if Helpers.isInteger token then
                // TODO: Is not enough to convert to int directly? Maybe these checks hurt performance?
                let value = token.Value<decimal> ()
                if value >= (decimal System.Int32.MinValue) && value <= (decimal System.Int32.MaxValue) then
                    Ok (LanguagePrimitives.EnumOfValue ((int32) value))
                else
                    (path, BadPrimitiveExtra("an int", token, "Value was either too large or too small for an int")) |> Error
            elif Helpers.isString token then
                match System.Int32.TryParse (Helpers.asString token, NumberStyles.Any, CultureInfo.InvariantCulture) with
                | true, x -> Ok <| LanguagePrimitives.EnumOfValue(x)
                | _ -> (path, BadPrimitive("an int", token)) |> Error
            else
                (path, BadPrimitive("an int", token)) |> Error

This works pretty good, however I would love to have generic Enum support, either direclty in Thoth.Json or as extra coder.

This naive approach fail:

module Encode =

   let enumInt (value: 'T when 'T:enum<int>) : JsonValue =
       LanguagePrimitives.EnumToValue (value)
       |>JValue :> JsonValue

module Decode =
    module Helpers =
        let inline isInteger (token: JsonValue) = not(isNull token) && (token.Type = JTokenType.Integer)
        let inline isString (token: JsonValue) = not(isNull token) && token.Type = JTokenType.String
        let inline asString (token: JsonValue): string = token.Value<string>()
    
    let enumInt : Decoder<'T> when 'T:enum<int> =
        fun path token ->
            if Helpers.isInteger token then
                // TODO: Is not enough to convert to int directly? Maybe these checks hurt performance?
                let value = token.Value<decimal> ()
                if value >= (decimal System.Int32.MinValue) && value <= (decimal System.Int32.MaxValue) then
                    Ok (LanguagePrimitives.EnumOfValue ((int32) value))
                else
                    (path, BadPrimitiveExtra("an int", token, "Value was either too large or too small for an int")) |> Error
            elif Helpers.isString token then
                match System.Int32.TryParse (Helpers.asString token, NumberStyles.Any, CultureInfo.InvariantCulture) with
                | true, x -> Ok <| LanguagePrimitives.EnumOfValue(x)
                | _ -> (path, BadPrimitive("an int", token)) |> Error
            else
                (path, BadPrimitive("an int", token)) |> Error

While the encoders and decoders work, they cannot be added withCustom, and I also failt adding them directly in Thoth.Json.Net.

It worked before in 2.x, maybe this just a special case of #9, where all unknown types case were handled by NewtonSoft.Json.

Remove the cache limitation when using generateDecoderCached

The idea is to remove this warning

/// ATTENTION: Use this only when other arguments (isCamelCase, extra) don't change

In order to do that, we could use the Type and arguments state in order to generate the key instead of just the cache.

Numbers are encoded as strings

Hi,

I just ran into an issue when attempting to decode json (Encoded with Thoth.Json.Net) using Elm's decoders: Thoth.Json.Net appears to encode numbers as strings whereas Elm's decoder is expecting number within the json.

Some code from Encode.fs:

let uint64 (value : uint64) : JsonValue = JValue(value.ToString(CultureInfo.InvariantCulture)) :> JsonValue

Is this an oversight or by design?
My expectation was that numbers would be encoded without quotes to the json.

NullRef in an optional decoder

Hi,

I have encountered the following inconsistency that I think might be a bug in Thoth.Json. Let's say we have an optional object in a JSON document. The field that holds it is decoded as optional. However, if I access some values of the object in its decoder (other than assigning them to some fields) the decoder crashes with a nullref. This happens only if the json document contains a key for the object that is assigned to a null literal. Here is a short repro of this behavior: https://gist.github.com/mnebes/12e51348d76440c5106202315494272a

I think one could fix that by slightly reordering the decodeMaybeNull function to first check for the null literal and only after that invoke the decoder like so:

-    let private decodeMaybeNull path (decoder : Decoder<'T>) value =
-       // The decoder may be an option decoder so give it an opportunity to check null values
-        match decoder path value with
-        | Ok v -> Ok(Some v)
-        | Error _ when Helpers.isNullValue value -> Ok None
-        | Error er -> Error er
+    let private decodeMaybeNull path (decoder : Decoder<'T>) value =
+        // The decoder may be an option decoder so give it an opportunity to check null values
+        if Helpers.isNullValue value then
+            Ok None
+        else
+            match decoder path value with
+            | Ok v -> Ok(Some v)
+            | Error er -> Error er

This works, but judging by the comment this is on purpose that the decoder is executed (though I can't quite imagine a situation where that would be effectively different than "short-circuiting" here?), so if you have any advice as to how to proceed it would be highly appreciated!

Thoth parses invalid JSON

#r "nuget: Thoth.Json.Net, 10.1.0"

open Thoth.Json.Net

let json =
  """
  {
    "x": 123,
    "y": 456,
  }
  """

match Decode.fromString Decode.value json with
| Ok _ ->
  printfn "The JSON is valid"
| Error error ->
  printfn $"The JSON is invalid: \n%s{error}"

The JSON is valid

This should not parse since the trailing comma is out of spec.

Update the build script to use paket.dependencies for generating the tests

In order to make it easier to work with the tests,

the build.fsx should be able to extract the info about the tests files from the paket.depencies file.

It should allow us to know the path under paket-files and also update the fsproj file.

Perhaps it's possible to generate a Meta.fsproj containing the Include statement for the tests files and then import Meta.fsproj in the Tests.fsproj file.

Like that we can just "clean" the info generated by paket in Tests.fsproj.

Enum with StringEnum should be lowercase, is uppercase

(moved from MangelMaxime/Thoth#133)

I have this enum:

[<StringEnum>]
type Camera = FirstPerson | ArcRotate | IsometricTopDown

When I serialize it on the server, it is serialized with UpperCase ("Camera":"FirstPerson").

Then on the client side it does not get recognized because Fable seems to expect lowerCase ("firstPerson").

Workaround 1:

I think in this case I can just remove the StringEnumbecause that value is not/(no longer) passed to any ts/js

Workaround 2:

disable lowercasing with [<StringEnum(CaseRules.None)>]

Fix 1:

The encoder on the server could detect the presence of StringEnum and check the case rules

Fix 2:

The decoder on the client could detect it and lowerCase the first char.


There doesn't seem to be any reflection info emitted for string enums, so either this would need to be extended in Fable, or fixed on the server side:

[<StringEnumAttribute>]
type MyStringEnum = A | B

type MyRecord = { I : int; E : MyStringEnum }

generates this reflection info:

export function MyRecord$reflection() {
  return record("Test.MyRecord", [], MyRecord, () => [["I", int32], ["E", string]]);
}

Helps needed to package correctly Thoth.Json.Net

Disclaimer: I don't know much about how nuspec and nuget package works. In general, they just work for me so I am fine. But with Thoth.Json.Net I have a more complex package and I am a bit lost.

@forki this issue is about my call for help on Twitter. I tried to sum up the different problem I have and what actions have been taken when so you can have a global vision of the situation. Sorry for the long issue but I tried to be as explicit as possible.

Problem n°1: The dependencies list doesn't reflect the difference depending on the targetted framework.

Since several version Thoth.Json.Net have the following depencies list:

Version 3.4.0
Capture d’écran 2019-06-11 à 09 23 06

Instead of something like that:

Version 3.0.0-beta-001
Capture d’écran 2019-06-11 à 09 23 28

This problem appeared when I tried to lower Newtonsoft.Json dependency to >= 11.0.2 instead of 12.0.1.

At that time, I switched from dotnet pack to paket pack using paket.template in project mode so it had the correct dependency for Newtonsoft.Json based on paket.dependencies.

In the past, @forki mentionned that if we didn't need different transitive deps in different framework it was "ok". Comment

However now, I need different deps depending on the Framework.

Capture d’écran 2019-06-11 à 09 29 41

See how .NETStandard 2.0 require Fable.Core now while .NETFramework 4.6 doesn't use it because Fable.Core is not available here and was generating exceptions.

In order to archieved this result, I use paket.template in file mode. Source

Problem n°2: It seems that even if we have Newtonsoft.Json >= 11.0.2 the dll was using/required 12.0.0.

@bentayloruk raised an issue about that #18 and because I didn't see any problem with releasing a new version solving his problem we added lowest_matching:true to paket.dependencies

Problem n°3: The AssemblyInfo of version 3.5.0-beta-001 seems to say that the AssemblyVersion is 1.0.0 which seems wrong.

I'm having some issues, but not sure why they are happening yet. Will investigate.

Side question, 3.5.0 beta 1 is 1.0.0.0 in all of the assembly version attributes. Is that intentional?
image

Originally posted by @bentayloruk in #19 (comment)

I suppose this is related to the changes I made when using paket.template in file mode. When doing so because the file mode of paket was asking the same info that I already had in my fsproj I removed them from that last file.

Diff of the changes made

My question is should we keep the info in both file? Should I use FAKE to generate the AssymblyInfo file and how can I use that generated file?

Problem n°4: Should we set an upper version restriction on the depencies?

Capture d’écran 2019-06-11 à 09 29 41

As you can see here Newtonsoft.Json is marked as >= 11.0.2 so people can use version 12+ of it.

While Fable.Core is marked as (>= 3.0.0 && < 4.0.0). What is the correct way to restrict dependencies?

Question - Case insensitive decode

I'm trying to parse a json object where the properties come from two different systems which have different fields with different case.

Here is a sample
system1:
[ {"id": "39336bd7-ecdc-4755-ac21-8f852d70d311", "data": ["one", "two", "three", "four"], "userid": "user22", "deviceid": "device123", "timestamp": 2345} ]

system2:
[ {"id": "39336bd7-ecdc-4755-ac21-8f852d70d312", "data": ["one", "two" ], "userId": "user122", "deviceId": "device123", "timestamp": 2346} ]

Have a look at userId and deviceId

Here is my decoder:

Decode.object
                    (fun get ->
                          {
                              Id = get.Required.Field "id" Decode.guid
                              Data = get.Required.Field "data" JObjectDecoder
                              DeviceId = get.Required.Field "deviceid" Decode.string
                              Timestamp = get.Required.Field "timestamp" Decode.int64
                              UserId = get.Required.Field "userid" Decode.string
                          }
                    )

How can I get it to support both cases?

Required field throwing null ref exception

I am trying to decode a mail message with email addresses from a json request.

Decoders are as follows:

open Thoth.Json.Net
open System.Net.Mail

/// Builds a System.Net.Mail.MailAddress from the json request.
let addressDecoder : Decoder<MailAddress> =
    Decode.object
        (fun get ->
            let email = get.Required.Field "email" Decode.string
            let display = get.Optional.Field "display" Decode.string |> Option.defaultValue email
            new MailAddress(email, display) )

/// Builds a System.Net.Mail.MailMessage from the json request.
let requestDecoder : Decoder<MailMessage> =
    Decode.object
        (fun get ->
            let msg = new MailMessage()
            get.Required.Field "to" (Decode.list addressDecoder) |> List.iter msg.To.Add
            get.Optional.Field "cc" (Decode.list addressDecoder) |> Option.defaultValue [] |> List.iter msg.CC.Add
            msg.From <- get.Required.Field "from" addressDecoder
            msg.Subject <- get.Required.Field "subject" Decode.string
            msg.Body <- get.Required.Field "body" Decode.string
            msg.IsBodyHtml <- get.Optional.Field "isBodyHtml" Decode.bool |> Option.defaultValue false
            msg )

It works if I pass in a valid json string:

"""
{ 
	"to": [ { "email": "[email protected]", "display": "Jordan Marr" } ],
	"from": { "email": "[email protected]" },
	"subject": "Test - optional fields",
	"body": "Hello, world."
}
"""
|> Decode.fromString requestDecoder

However, if I leave out the required "from" email address, it fails with a null reference exception.

"""
{ 
	"to": [ { "email": "[email protected]", "display": "Jordan Marr" } ],
	"zzzzzfrom": { "email": "[email protected]" },
	"subject": "Test - optional fields",
	"body": "Hello, world."
}
"""
|> Decode.fromString requestDecoder

My expectation is that the decoder would safely return an Error Result saying that "from" is a required field.

Similarly, if I leave out the "to" field, it will fail with a null reference exception when I am expecting an Error Result.

Fable 4 Support

I'd like to upgrade a Fable project from Fable 3 to Fable 4.
This package currently requires Fable.Core >= 3.1.6 and < 4.0. Which makes it not possible to upgrade to Fable 4.

I'm wondering the following:

  • Why does this package have a dependency on Fable.Core?
  • Can we lift the requirement that the Fable.Core version has to be lower than Fable 4?

Decode.string fails on strings with datetime

Original issue: MangelMaxime/Thoth#142
by @dbrattli

Looks like Decode.string fails with this string. Using Thoth.Json.Net (3.1) Newtonsoft.Json (12.0.1). Anyone that can repro this?

Decode.fromString Decode.string "\"2018-08-06T10:06:40.000\""
"Error at: `$` Expecting a string but instead got: "2018-08-06T10:06:40""

Probably because it's a datetime, perhaps related to https://stackoverflow.com/questions/11856694/json-net-disable-the-deserialization-on-datetime

Int64 encoded as string

Currently, Thoth encodes int64 as a string i.e:

(Encode.list [ Encode.int64 42L ] |> Encode.toString 0) = "[\"42\"]"

Which might not be what you expect, and most importantly it might not be what the receiver expect. Int64 allows values up to 9223372036854775807 while JSON safe numbers are up to 9007199254740991. JValue will accept Int64.MaxValue, so not sure why we encode them as string? I would suggest that we encode int64 directly using JValue(value) :> JsonValue and instead document that numbers above 9007199254740991 might be unsafe for the receiver? At least i think it's safer than sending strings by default, or?

Question: How to reference both NuGet packages?

How to reference both NuGet packages ?
My library should work on .NET and JS in the fsproj I have:

  <ItemGroup Condition="$(DefineConstants.Contains('FABLE_COMPILER'))">
    <PackageReference Include="Thoth.Json" Version="10.2.0" />
  </ItemGroup>

  <!-- a Not-Contains-Condition with leading exclamation mark:  -->
  <ItemGroup Condition="!$(DefineConstants.Contains('FABLE_COMPILER'))">
    <PackageReference Include="Thoth.Json.Net" Version="11.0.0" />
  </ItemGroup>

Is there another way to reference both packages?

then in the code :

#if FABLE_COMPILER
    open Thoth.Json
#else
    open Thoth.Json.Net
#endif

    Encode.Auto.toString(..)

When compiled with Fable 4.14 fails with
The value, namespace, type or module 'Encode' is not defined. (code 39)

`Thoth.Json.Net` cannot find Union case

Issue by matthid
Thursday Oct 24, 2019 at 13:45 GMT
Originally opened as MangelMaxime/Thoth#175


open Fable.Core
open Thoth.Json.Net

[<StringEnum>]
type Test = 
 | Test1
 | TEST2

[<EntryPoint>]
let main argv =

    let t = TEST2
    printfn "%A" t
    let json = (Encode.Auto.toString(0, t))
    printfn "json %A" json
    let t2 : Test = Decode.Auto.unsafeFromString(json)
    printfn "res %A" t2

    0 // return an integer exit code

image

Int64 encoded as string

I can see the #16 was closed like this:

@MangelMaxime wrote:

I don't see any problem with using string to represent int64 in JSON.

Our data provider requires us to send:

{
    "command": "getTickPrices",
    "arguments": {
        "timestamp": 1637589093796
    }
}

1637589093796 is out of range for int, so how am I to create a valid JSON request? They won't accept "1637589093796". This default behavior is a trouble, how to work it around?

Decode.fromString can throw exceptions

Hi,

I may be misunderstanding Decode.fromString, but the implementation and its unsafe counterpart seems to suggest that fromString should not throw, ever. However, I am able to get it to throw by giving it some bad data:

System.Reflection.TargetParameterCountException: 'Parameter count mismatch.'
This exception was originally thrown at this call stack:
    Thoth.Json.Net.Decode.makeUnion(Microsoft.FSharp.Collections.FSharpMap<string, Microsoft.FSharp.Core.FSharpRef<Thoth.Json.Net.BoxedDecoder>>, bool, System.Type, string, string, Newtonsoft.Json.Linq.JToken[])
    [email protected](string, Newtonsoft.Json.Linq.JToken)
    Thoth.Json.Net.Decode.DecoderCrate<T>.Decode(string, Newtonsoft.Json.Linq.JToken)
    Thoth.Json.Net.Decode.AutoModule.generateDecoder@1289<T>.Invoke(string, Newtonsoft.Json.Linq.JToken)
    Thoth.Json.Net.Decode.fromValue<T>(string, Microsoft.FSharp.Core.FSharpFunc<string, Microsoft.FSharp.Core.FSharpFunc<Newtonsoft.Json.Linq.JToken, Microsoft.FSharp.Core.FSharpResult<T, System.Tuple<string, Thoth.Json.Net.ErrorReason>>>>, Newtonsoft.Json.Linq.JToken)
    Thoth.Json.Net.Decode.fromString<T>(Microsoft.FSharp.Core.FSharpFunc<string, Microsoft.FSharp.Core.FSharpFunc<Newtonsoft.Json.Linq.JToken, Microsoft.FSharp.Core.FSharpResult<T, System.Tuple<string, Thoth.Json.Net.ErrorReason>>>>, string)

All I did to reproduce this is leave off a leading [ from my input string, which is being decoded into a single case DU. Is this a bug in Thoth, or something I'm doing wrong? Obviously I can simply catch this or all exceptions and assume the decoding failed, but I'm wondering why I need to do that when fromString already returns a Result<>.

2.5 -> 3.0 can encode less objects automatically

I use thoth.json.net as the default json encoder, because that's how the safe template was structured:

https://github.com/SAFE-Stack/SAFE-BookStore/blob/master/src/Server/Program.fs#L45

With 2.5, everything worked fine.

Now I've updated to 3.0, and have two issues:

  1. it can't serialize C# POCOs
  2. It can no longer serialize records containing interfaces, even when wrapped in an option, and the actual value is none.

In both cases, the error message is Cannot generate auto encoder for MyType. Please pass an extra encoder.


For 1), this only kinda accidentally uses Thoth because it's registered as the default, it is actually used for an web api that is consumed by a C# client, and not for fable interop.

  1. is used in Fable interop, here is a related issue on the client side: MangelMaxime/Thoth#132 (comment)

What is your opinion on this?

Is registering thoth as the default asp.net core serializer actually a good idea, or should I change this back to newtonsoft?

And for 2, would you be open to fixing this so it supports interface options, as long as they are None?

Should Thoth.Json.Net.dll in the nupkg lib reference the minimum dependency version of Newtonsoft.Json?

Reposting this issue here as originally submitted in the old Issues repos.

Hi Thothers!

I've tried to figure this out, but failed, so hoping someone in here will have the knowledge.

I have a problem which I think stems from the fact that the Thoth.Json.Net nuspec specifies <dependency id="Newtonsoft.Json" version="11.0.2" /> but the nupkg/lib/netstandard2.0/Thoth.Json.Net.dll references version 12.0.0.0.

Here is the nuspec for 3.0.0:
image

And here is the lib reference (via ILSpy):
image

In our case, Thoth.Json.Net is a dependency of our first assembly A and then we reference A from our second assembly B. We then reference assembly B from a web project, where the dependencies are managed by Nuget, not Paket. In this situation, Thoth.Json.Net is a transitive dependency of our web application and as a result, so is Newtonsoft.Json. Nuget correctly identifies this, reads the nuspec for Thoth and based on that decides we are fine with version 11.0.2 of Newtonsoft.Json (I think Nuget defaults to the earliest version of the dependency?). However, at runtime our web application fails because Thoth.Json.Net tries to load version 12.0.0.0.

I've done a bunch of Googling, but have been unable to determine how I'm supposed to handle this in the .NET Core world (in the old world I might have tried a "downgrade" assembly binding). On the surface it would seem sensible to me that the lib distribution of Thoth.Json.Net should reference the minimum version of it's dependencies in order to avoid this, but maybe I'm missing something and this is a scenario that should be fine? If so, does anyone know the incantations I need to mumble?

I'm currently working around this by adding version 12 of Newtonsoft as a top level PackageReference in my web project, but that is not ideal.

How to contribute genericSeq implementation?

Hello!

I think I have figured out how to implement an Auto decoder for seq<'t>:

    open System
    open System.Collections
    open System.Collections.Generic

    type private UnboxedSeq<'t> (inner : obj seq) =
        interface seq<'t> with
          member this.GetEnumerator() : IEnumerator<'t> =
            (seq {
              for x in inner do
                yield x :?> 't
            }).GetEnumerator()

          member this.GetEnumerator() =
            (this :> seq<'t>).GetEnumerator() :> IEnumerator

    let private genericSeq (elementT : Type) (decoder: BoxedDecoder) =
        let unboxedSeqT = typedefof<UnboxedSeq<_>>
        let seqT = unboxedSeqT.MakeGenericType(elementT)

        fun (path : string) (value: JsonValue) ->
            if not (Helpers.isArray value) then
                (path, BadPrimitive ("a seq", value)) |> Error
            else
                let values = value.Value<JArray>()
                (values, Ok []) ||> Seq.foldBack (fun value acc ->
                    match acc with
                    | Error _ -> acc
                    | Ok acc ->
                        match decoder.Decode(path, value) with
                        | Error er -> Error er
                        | Ok result -> result :: acc |> Ok)
                |> Result.map (fun xs -> Activator.CreateInstance(seqT, xs))

However, I'm not sure how to contribute it.

Should I add tests to this repo first?

Cannot serialized higher order type when type is boxed

This code throws System.ArgumentException: 'Could not determine JSON object type for type Program+MyRecord.'

type MyRecord = {
    Field1 : string
    Field2 : int
    Field3 : bool
}
type Wrapper<'a> = {
    Id:     string
    Config: 'a
}
let myObject = {
    Id = "test test test";
    Config = (box {Field1 = "hi"; Field2 = 2; Field3 = true })
}
let result = JsonConvert.SerializeObject(myObject, Converters.Converter())

If we remove the box, the serialization works but sadly in my case I cannot avoid it.

Throws exception for very large JSON numbers

Whenever I use Decode.fromString, I expect it to return either Ok or Error and never throw exceptions.

However, if the JSON contains a really large number, a System.InvalidCastException exception is thrown.

The various int decoders do have bounds checking, and that works to some extent. But if the number is too crazy large, even the bounds check don’t help.

If this is a problem with how Newtonsoft does things, maybe add a try-with in Thoth.Json.Net?

❯ dotnet fsi

Microsoft (R) F# Interactive version 12.7.0.0 for F# 7.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> #r "nuget: Thoth.Json.Net, 11.0.0";;
[Loading /Users/simon/.packagemanagement/nuget/Cache/2fd0b2ac9e4082b4ba06da12786fc9dc357957f471b12f92af911c26d54f0284.fsx]
module FSI_0002.
       2fd0b2ac9e4082b4ba06da12786fc9dc357957f471b12f92af911c26d54f0284

> open Thoth.Json.Net;;

-- int fails:
> Decode.fromString Decode.int "9999999999999999999";;
System.InvalidCastException: Object must implement IConvertible.
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at Newtonsoft.Json.Linq.Extensions.Convert[T,U](T token)
   at Newtonsoft.Json.Linq.Extensions.Value[T,U](IEnumerable`1 value)
   at Newtonsoft.Json.Linq.Extensions.Value[U](IEnumerable`1 value)
   at [email protected](String path, JToken value)
   at Thoth.Json.Net.Decode.fromValue[T](String path, FSharpFunc`2 decoder, JToken value)
   at Thoth.Json.Net.Decode.fromString[T](FSharpFunc`2 decoder, String value)
   at <StartupCode$FSI_0004>.$FSI_0004.main@() in /Users/simon/tmp/stdin:line 4
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
Stopped due to error

-- Let’s try int64 instead. Also fails:
> Decode.fromString Decode.int64 "9999999999999999999";;
System.InvalidCastException: Object must implement IConvertible.
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at Newtonsoft.Json.Linq.Extensions.Convert[T,U](T token)
   at Newtonsoft.Json.Linq.Extensions.Value[T,U](IEnumerable`1 value)
   at Newtonsoft.Json.Linq.Extensions.Value[U](IEnumerable`1 value)
   at [email protected](String path, JToken value)
   at Thoth.Json.Net.Decode.fromValue[T](String path, FSharpFunc`2 decoder, JToken value)
   at Thoth.Json.Net.Decode.fromString[T](FSharpFunc`2 decoder, String value)
   at <StartupCode$FSI_0005>.$FSI_0005.main@() in /Users/simon/tmp/stdin:line 5
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
Stopped due to error

-- Removing one `9`, it’s a valid in64:
> Decode.fromString Decode.int64 "999999999999999999";;
val it: Result<int64,string> = Ok 1000000000000000000L

-- And then, when using just int the bounds check works:
> Decode.fromString Decode.int "999999999999999999";;
val it: Result<int,string> =
  Error
    "Error at: `$`
Expecting an int but instead got: 999999999999999999
Reason: Value was either too large or too small for an int"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.