Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In aeson, how do you encode from a Value without resulting in scientific notation?

Tags:

haskell

aeson

I have parsed a large amount of json, manipulated some values and I'd like to write it back out. Aeson decodes numbers into scientific, but when it encodes it, by default, scientific shows numbers in scientific notation in many cases, and aeson does not offer any means that I can see to change that.

> decode "[\"asdf\", 1, 1.0, 1000000000.1, 0.01]" :: Maybe Value
Just (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])

encode (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
"[\"asdf\",1,1,1.0000000001e9,1.0e-2]"

> encode (Array [String "asdf", Number 1, Number 1.0, Number 1000000000.1, Number 0.01])
"[\"asdf\",1,1,1.0000000001e9,1.0e-2]"

How can I write out my Value with numbers in a more widely acceptable format that other languages can consume? Let's pretend I'm not concerned with precision loss or integer overflows. The scientific package has the means to format numbers in this manner, aeson just happened not to use it.

>formatScientific Fixed Nothing (0.01)
"0.01"

>formatScientific Fixed Nothing (1000000000.1)
"1000000000.1"
like image 686
David McHealy Avatar asked Sep 07 '16 17:09

David McHealy


2 Answers

The pretty printer for Aeson (only since version 0.8, quite recent) has a configuration option that does exactly what you're asking:

Prelude> :m +Data.Aeson Data.Aeson.Encode.Pretty 
Prelude Data.Aeson Data.Aeson.Encode.Pretty> encodePretty' (defConfig{confNumFormat=Decimal}) (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
"[\n    \"asdf\",\n    1.0,\n    1.0,\n    1000000000.1,\n    0.01\n]"

Unfortunately, this pretty-printer now outputs the actual integral numbers as 1.0 instead of 1, which I consider a much more severe problem than printing decimals in scientific notation (which really should be ok everywhere). This also happens with the default setting:

Prelude Data.Aeson Data.Aeson.Encode.Pretty> encodePretty (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
"[\n    \"asdf\",\n    1.0,\n    1.0,\n    1.0000000001e9,\n    1.0e-2\n]"

Version 0.7.2 still printed integers without fractional part, like the normal aeson encode does:

aeson-pretty-0.7.2:Data.Aeson.Encode.Pretty> encodePretty (Array [String "asdf",Number 1.0,Number 1.0,Number 1.0000000001e9,Number 1.0e-2])
"[\n    \"asdf\",\n    1,\n    1,\n    1.0000000001e9,\n    1.0e-2\n]"

I think I'll file this as a bug report.

Note that there's also a Custom NumberFormat option, which gives you complete control about how numbers should appear.

like image 80
leftaroundabout Avatar answered Nov 15 '22 11:11

leftaroundabout


I have looked for this functionality before myself; I believe at that time I was trying to interface with a Ruby program, and the standard Ruby JSON parser didn't support scientific notation. Unfortunately I believe aeson does not have a way to do it, so you will need to either patch aeson or write your own encoder.

There are further details on the aeson issue tracker. That issue additionally suggests using toEncoding on a type other than Value to achieve this, but it's not clear whether this is practical in your case or not.

like image 41
Daniel Wagner Avatar answered Nov 15 '22 11:11

Daniel Wagner