fix: TOML encoder uses inline tables for YAML FlowStyle mappings, inconsistent with explicit JSON parsing (#2687)

* Initial plan

* fix: TOML encoder no longer treats YAML FlowStyle as inline tables

Remove FlowStyle checks from the TOML encoder. YAML flow-style mappings
are a YAML-specific rendering hint and should not influence TOML output.
Only nodes explicitly marked with EncodeHintInline (set by the TOML
decoder for actual TOML inline tables) will produce TOML inline table
syntax.

This fixes the bug where JSON auto-detected via the YAML parser (which
parses {} as flow-style mappings) would produce inline TOML tables
instead of readable table sections, while explicitly parsing with
-p json produced correct table sections.

Updated tests: YAML flow mappings now produce table sections (same as
block mappings), consistent with the fix. Added new test cases for the
JSON → TOML conversion via both YAML decoder (auto-detection) and JSON
decoder.

Agent-Logs-Url: https://github.com/mikefarah/yq/sessions/3e504870-b585-4998-af9c-a451e2f6a6a3

Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mikefarah <1151925+mikefarah@users.noreply.github.com>
This commit is contained in:
Copilot 2026-04-28 19:32:07 +10:00 committed by GitHub
parent cfe2eee7e6
commit cb97935554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 9 deletions

View File

@ -184,9 +184,12 @@ func (te *tomlEncoder) encodeTopLevelEntry(w io.Writer, path []string, node *Can
// Regular array attribute
return te.writeArrayAttribute(w, path[len(path)-1], node)
case MappingNode:
// Use inline table syntax for nodes explicitly marked as TOML inline tables
// or YAML flow mappings. All other mappings become readable TOML table sections.
if node.EncodeHint == EncodeHintInline || node.Style&FlowStyle != 0 {
// Use inline table syntax only for nodes explicitly marked as TOML inline tables.
// YAML flow-style mappings are not treated as inline tables; the FlowStyle attribute
// is a YAML-specific rendering hint and should not affect TOML output. This ensures
// that auto-detected JSON input (parsed as YAML flow style) produces readable table
// sections, consistent with explicitly parsed JSON input.
if node.EncodeHint == EncodeHintInline {
return te.writeInlineTableAttribute(w, path[len(path)-1], node)
}
return te.encodeSeparateMapping(w, path, node)
@ -469,7 +472,7 @@ func (te *tomlEncoder) encodeSeparateMapping(w io.Writer, path []string, m *Cand
hasAttrs = true
break
}
if v.Kind == MappingNode && (v.EncodeHint == EncodeHintInline || v.Style&FlowStyle != 0) {
if v.Kind == MappingNode && v.EncodeHint == EncodeHintInline {
hasAttrs = true
break
}
@ -569,13 +572,13 @@ func (te *tomlEncoder) encodeMappingBodyWithPath(w io.Writer, path []string, m *
}
}
// Finally, child mappings: inline-hint or flow-style ones become inline table attributes,
// Finally, child mappings: inline-hint ones become inline table attributes,
// while all others are emitted as separate sub-table sections.
for i := 0; i < len(m.Content); i += 2 {
k := m.Content[i].Value
v := m.Content[i+1]
if v.Kind == MappingNode {
if v.EncodeHint == EncodeHintInline || v.Style&FlowStyle != 0 {
if v.EncodeHint == EncodeHintInline {
if err := te.writeInlineTableAttribute(w, k, v); err != nil {
return err
}

View File

@ -707,11 +707,25 @@ var tomlScenarios = []formatScenario{
},
{
skipDoc: true,
description: "Encode: YAML flow mapping stays inline",
description: "Encode: YAML flow mapping produces table section (same as block mapping)",
input: "arg: {hello: foo}\n",
expected: "arg = { hello = \"foo\" }\n",
expected: "[arg]\nhello = \"foo\"\n",
scenarioType: "encode",
},
{
skipDoc: true,
description: "Issue: JSON auto-detected via YAML decoder produces table sections",
input: `{"arg":{"hello": "foo"}}`,
expected: "[arg]\nhello = \"foo\"\n",
scenarioType: "encode",
},
{
skipDoc: true,
description: "Issue: JSON via JSON decoder produces table sections",
input: `{"arg":{"hello": "foo"}}`,
expected: "[arg]\nhello = \"foo\"\n",
scenarioType: "encode-json",
},
{
skipDoc: true,
description: "Roundtrip: key with special characters in inline table",
@ -753,6 +767,8 @@ func testTomlScenario(t *testing.T, s formatScenario) {
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewTomlDecoder(), NewTomlEncoder()), s.description)
case "encode":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewTomlEncoder()), s.description)
case "encode-json":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewTomlEncoder()), s.description)
}
}
@ -833,7 +849,7 @@ func documentTomlScenario(_ *testing.T, w *bufio.Writer, i interface{}) {
documentTomlDecodeScenario(w, s)
case "roundtrip":
documentTomlRoundtripScenario(w, s)
case "encode":
case "encode", "encode-json":
documentTomlEncodeScenario(w, s)
default: