Repository navigation
print: a simpler JSON schema for export #2650
Description
Related to #1099, #1195, and others:
There's common perception that the JSON output of print (and maybe other reports) is complex and harder-than-necessary for humans to work with. It is a bit complex, because it tries to represent the internal data types faithfully and losslessly, as far as possible.
Chunhui Ouyang has been working on a simpler, easier-to-consume JSON schema, in chat. Here is the latest draft as of today:
[
{
"code": "",
"comment": "",
"date": "",
"date2": "",
"payee": "",
"description": "",
"status": "",
"tags": {
"key1": "value1",
"key2": "value2"
},
"postings": [
{
"account": "USD",
"amount": [
{
"commodity": "",
"cost": {
"commodity": "",
"display_value": "",
"value": ""
},
"display_value": "1000.53",
"value": "1000.52860"
}
],
"comment": "",
"tags": {
"key1": "value1",
"key2": "value2"
}
}
]
}
]The main difference from the current hledger print -O json schema is a simpler (and more lossy, because of using json's native numbers) representation of amounts.
Since print is often used for export, not just reporting, I feel the lossiness might be a problem (disrupting balanced transactions, balance assertions, etc.) But it's hard to say how much of a problem, without some testing.
Someone could add this alternate json schema to print fairly easily. (We need to keep the existing non-lossy schema also.) Call it print -O json2 or something for now. I think what it needs next is an app using it, with real world data, so that we could get some experience with it and judge its effectiveness.
Note, simple/easier to work with is the goal here. A related but different goal could be "use (or be more like) a standard schema used by others". We searched briefly and didn't find an obvious one of those. Microsoft Common Data Model and XBRL (eXtensible Business Reporting Language) were some possibilities.
Activity
simonmichael commentedon Jun 19, 2026
Related:
simonmichael commentedon Jun 19, 2026
For comparison, here is
print's current json schema (this time with some real data):[ { "tcode": "", "tcomment": "", "tdate": "2026-01-01", "tdate2": null, "tdescription": "opening balances", "tindex": 1, "tpostings": [ { "paccount": "assets:usd", "pamount": [ { "acommodity": "USD", "acost": null, "acostbasis": null, "aquantity": { "decimalMantissa": 10, "decimalPlaces": 0, "floatingPoint": 10 }, "astyle": { "ascommodityside": "R", "ascommodityspaced": true, "asdecimalmark": ".", "asdigitgroups": null, "asprecision": 0, "asrounding": "NoRounding" } } ], "pbalanceassertion": null, "pcomment": "", "pdate": null, "pdate2": null, "poriginal": null, "preal": "RealPosting", "pstatus": "Unmarked", "ptags": [], "ptransaction_": "1" }, { "paccount": "assets:eur", "pamount": [ { "acommodity": "EUR", "acost": null, "acostbasis": null, "aquantity": { "decimalMantissa": 20, "decimalPlaces": 0, "floatingPoint": 20 }, "astyle": { "ascommodityside": "R", "ascommodityspaced": true, "asdecimalmark": ".", "asdigitgroups": null, "asprecision": 0, "asrounding": "NoRounding" } } ], "pbalanceassertion": null, "pcomment": "", "pdate": null, "pdate2": null, "poriginal": null, "preal": "RealPosting", "pstatus": "Unmarked", "ptags": [], "ptransaction_": "1" }, { "paccount": "assets:equity", "pamount": [ { "acommodity": "EUR", "acost": null, "acostbasis": null, "aquantity": { "decimalMantissa": -20, "decimalPlaces": 0, "floatingPoint": -20 }, "astyle": { "ascommodityside": "R", "ascommodityspaced": true, "asdecimalmark": ".", "asdigitgroups": null, "asprecision": 0, "asrounding": "NoRounding" } }, { "acommodity": "USD", "acost": null, "acostbasis": null, "aquantity": { "decimalMantissa": -10, "decimalPlaces": 0, "floatingPoint": -10 }, "astyle": { "ascommodityside": "R", "ascommodityspaced": true, "asdecimalmark": ".", "asdigitgroups": null, "asprecision": 0, "asrounding": "NoRounding" } } ], "pbalanceassertion": null, "pcomment": "", "pdate": null, "pdate2": null, "poriginal": null, "preal": "RealPosting", "pstatus": "Unmarked", "ptags": [], "ptransaction_": "1" } ], "tprecedingcomment": "", "tsourcepos": [ { "sourceColumn": 1, "sourceLine": 1, "sourceName": "/Users/simon/src/hledger/a.j" }, { "sourceColumn": 1, "sourceLine": 5, "sourceName": "/Users/simon/src/hledger/a.j" } ], "tstatus": "Unmarked", "ttags": [] } ]jack9603301 commentedon Jun 19, 2026
It is worth noting that this new mode uses string representation to represent numbers, so it can express decimals of any precision.
jack9603301 commentedon Jun 20, 2026
The above is the v2 draft. Although I believe that string notation may be simple enough and sufficient for JSON representation, for improvement purposes, there is a v3 draft here. It is an upgrade of v2 and uses scientific notation to faithfully represent the internal representation structure of large number algorithms. The following is the v3 draft:
The following is an example of the internal representation of scientific notation in v3.
The reason I'm writing this here is for record-keeping purposes. If I need it someday, I can quickly find it and won't forget it.
This is just a record; it seems that both @simonmichael and I want to adopt the v2 expression because it aligns with the goals of this issue.
Scientific notation follows the formula:
base-k: Indicates the segmentation of the integer part of a large number; for example, a 20-digit integer is forcibly split into two segments. In the formula, it is the value of k.exponent: Indicates the exponent and the decimal point position.segments: Data segments, following the formula:Verification of
bc: