Search code examples
javascriptjsontoml

Why is `TOML.stringify()` output different for equivalent JSON input


TOML.parse() seems to output a JSON object that somehow "remembers" the original TOML formatting. When the JSON is sent back to TOML.stringify() the original formatting is preserved. However when this JSON is "purified" by stringifying and parsing back to JSON, the formatting is no longer preserved.

Origininal TOML:

# Tables defined in three different ways A,B,C:
parsedFromTomlA.titles = ['A1', 'A2']
parsedFromTomlB.titles = [
   'B1', 'B2'
]
[parsedFromTomlC]
titles = ['C1', 'C2']

TOML.stringify(TOML.parse(toml)):

parsedFromTomlA.titles = ['A1', 'A2']
parsedFromTomlB.titles = [
    'B1',
    'B2',
]
fromObjectLiteralD.titles = [
    'D1',
    'D2',
]

[parsedFromTomlC]

titles = ['C1', 'C2']

TOML.stringify(JSON.parse(JSON.stringify(TOML.parse(toml)))):

parsedFromTomlA.titles = [
    'A1',
    'A2',
]
parsedFromTomlB.titles = [
    'B1',
    'B2',
]
parsedFromTomlC.titles = [
    'C1',
    'C2',
]
fromObjectLiteralD.titles = [
    'D1',
    'D2',
]

How is j-toml "remembering" the format of the original TOML? How can I force j-toml to use a certain format for object literals that are added to the JSON?


Test this code on RunKit:

var TOML = require("@ltd/j-toml")

const toml = `
parsedFromTomlA.titles = ['A1', 'A2']
parsedFromTomlB.titles = [
   'B1', 'B2'
]
[parsedFromTomlC]
titles = ['C1', 'C2']
`
const jsonParseToml = TOML.parse(toml)

// Add object literal.
jsonParseToml.fromObjectLiteralD = {
   titles: ['D1', 'D2']
}

// "Purify" JSON by converting to text, then back to JSON.
const jsonParseJson = JSON.parse(JSON.stringify(jsonParseToml))

console.log('Objects appear the same:')
console.log('jsonParseToml', JSON.stringify(jsonParseToml, null, 4))
console.log('jsonParseJson', JSON.stringify(jsonParseJson, null, 4))

// Convert objects back to TOML.
const tomlOutParseToml = TOML.stringify(jsonParseToml, {newline: '\n'})
const tomlOutParseJson = TOML.stringify(jsonParseJson, {newline: '\n'})

console.log('But TOML output is different:')
console.log('tomlOutParseToml', tomlOutParseToml)
console.log('tomlOutParseJson', tomlOutParseJson)

Solution

  • Answered by library author:

    This library use WeakMap internal to remember object formats parsed by same library. For objects from JSON.parse or other way created, you may mark each object by helper functions provided by the library, because I didn't find a better way to know what coder want.

    See details in docs: [link]

    I was able to use the TOML.Section() helper to force JSON.stringify() to look more like parsedFromTomlC.


    Interestingly, this may also explain why functions that require a serializable JSON object sometimes result in an error when passed output from TOML.parse().