在 aeson 中,如何在不产生科学记数法的情况下从值进行编码?

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

我已经解析了大量 json,操纵了一些值,我想将其写回。 Aeson 将数字解码为科学数字,但当它对其进行编码时,默认情况下,科学在许多情况下以科学计数法显示数字,而 aeson 没有提供任何我可以看到的方式来改变它。

> 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]"

如何以其他语言可以使用的更广泛接受的格式用数字写出我的值?假设我不关心精度损失或整数溢出。 scientific 包有这种格式化数字的方法,aeson 只是碰巧没有使用它。

>formatScientific Fixed Nothing (0.01)
"0.01"

>formatScientific Fixed Nothing (1000000000.1)
"1000000000.1"

我之前已经寻找过这个功能;我相信当时我正在尝试与 Ruby 程序交互,而标准 Ruby JSON 解析器不支持科学记数法。不幸的是,我相信 aeson 没有办法做到这一点,所以你需要给 aeson 打补丁或编写你自己的编码器。

还有更多详细信息on the aeson issue tracker。该问题还建议在 Value 以外的类型上使用 toEncoding 来实现此目的,但目前尚不清楚这对您的情况是否可行。

pretty printer for Aeson(仅从 0.8 版开始,非常新)有一个配置选项可以完全满足您的要求:

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]"

不幸的是,这个漂亮的打印机现在将实际整数输出为 1.0 而不是 1,我认为这比用科学记数法打印小数(这实际上应该是到处都可以)。默认设置也会发生这种情况:

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]"

版本 0.7.2 仍然打印没有小数部分的整数,就像正常的 aeson encode 一样:

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]"

我想我会将其作为错误报告归档。

请注意,还有一个 Custom NumberFormat 选项,可让您完全控制数字的显示方式。