ahrefs / atd

Static types for JSON APIs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Should it be possible to round-trip json?

rbjorklin opened this issue · comments

I was caught by surprise when trying to round-trip some json following this example in the documentation. Is round-tripping supposed to work?

let json = "{\"ID\":12345678,\"username\":\"kimforever\",\"background_color\":\"black\"}";;

let profile = Profile_j.profile_of_string json;;

let round_tripped_json = Profile_j.string_of_profile profile;;

(* "{\"ID\":12345678,\"username\":\"kimforever\",\"background_color\":<\"black\">}" *)

Notice how "black" comes back as <\"black\">.

The same behaviour is also present when using <ocaml repr="classic">.

Can you try compiling the atd with -j-std? (IMO it should be the default)

Yeah that fixed it! Thanks for the quick response @cyberhuman! This feels like it might be worth a mention somewhere close to where variants are shown.

EDIT: Now that I know what I'm looking for I found the relevant documentation. Unfortunately I have no good suggestion for how to make this more discoverable.

Can you try compiling the atd with -j-std? (IMO it should be the default)

Yes. The only issue is backward compatibility. We might want to release this as part of a major version release. I started a list here.

On the topic of round-tripping is there a way to work-around this behaviour?

my_type.atd:

type my_type = {
    my_number : float;
    my_key : string;
}

my_type_test.ml:

let t = My_type_t.{ my_number = 65.26; my_key = "ABC" }

let () = 
   Printf.printf "%f" t.my_number;
    (* Prints: "65.260000" *)

    Printf.printf "%s" (My_type_j.string_of_my_type t)
    (* Prints: "{\"my_number\":65.26000000000001,\"my_key\":\"ABC\"}" *)

I would be okay parsing my_number into a string as long as I can round-trip it to a json number while maintaining the number of significant figures.

It's best to open a new issue for each new question.

Printing 65.26 instead of 65.26000000000001 while maintaining correctness is difficult or computationally expensive. Atdgen prints enough decimals to ensure that parsing the string representing the float will result in the original float. In this case, you can see that it works:

utop # 65.26 = 65.26000000000001;;
- : bool = true

The internal representation of OCaml floats or C doubles (IEEE-754 binary-64) is binary rather than decimal and provides no clue that there's a short decimal representation for the number. Printing a sufficiently large number of decimal digits (17 digits or so) ensures that parsing the string in decimal notation will recover the original float.

Maybe there are tricks we could use to print 65.26 as 65.26 correctly and cheaply but I'm not an expert in this field and I'm not aware of such tricks.