5

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"
David McHealy
  • 2,471
  • 18
  • 34
  • 3
    If other languages can't consume it, it's a problem with their JSON parser. [Scientific notation is part of the JSON standard.](http://www.json.org/) – Cirdec Sep 07 '16 at 17:10
  • 1
    Noted. I'd still like to know if there is a way to do it without writing my own encode function. – David McHealy Sep 07 '16 at 17:26

2 Answers2

4

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.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • I should be able to extract some minimal code from the library at least. If no one gives a better method I'll mark it answered. – David McHealy Sep 07 '16 at 18:19
1

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.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380