Fixed json decode to maintain key order

This commit is contained in:
Mike Farah 2022-08-01 16:26:43 +10:00
parent a91a8ccc66
commit 2c9b5be408
3 changed files with 57 additions and 27 deletions

View File

@ -22,13 +22,13 @@ func (dec *jsonDecoder) Init(reader io.Reader) {
func (dec *jsonDecoder) Decode(rootYamlNode *yaml.Node) error { func (dec *jsonDecoder) Decode(rootYamlNode *yaml.Node) error {
var dataBucket interface{} var dataBucket orderedMap
log.Debug("going to decode") log.Debug("going to decode")
err := dec.decoder.Decode(&dataBucket) err := dec.decoder.Decode(&dataBucket)
if err != nil { if err != nil {
return err return err
} }
node, err := dec.convertToYamlNode(dataBucket) node, err := dec.convertToYamlNode(&dataBucket)
if err != nil { if err != nil {
return err return err
@ -38,39 +38,36 @@ func (dec *jsonDecoder) Decode(rootYamlNode *yaml.Node) error {
return nil return nil
} }
func (dec *jsonDecoder) convertToYamlNode(data interface{}) (*yaml.Node, error) { func (dec *jsonDecoder) convertToYamlNode(data *orderedMap) (*yaml.Node, error) {
switch data := data.(type) { if data.kv == nil {
case nil: switch rawData := data.altVal.(type) {
return createScalarNode(nil, "null"), nil case nil:
case float64, float32: return createScalarNode(nil, "null"), nil
// json decoder returns ints as float. case float64, float32:
return parseSnippet(fmt.Sprintf("%v", data)) // json decoder returns ints as float.
case int, int64, int32, string, bool: return parseSnippet(fmt.Sprintf("%v", rawData))
return createScalarNode(data, fmt.Sprintf("%v", data)), nil case int, int64, int32, string, bool:
case map[string]interface{}: return createScalarNode(rawData, fmt.Sprintf("%v", rawData)), nil
return dec.parseMap(data) case []*orderedMap:
case []interface{}: return dec.parseArray(rawData)
return dec.parseArray(data) default:
default: return nil, fmt.Errorf("unrecognised type :( %v", rawData)
return nil, fmt.Errorf("unrecognised type :(") }
} }
}
func (dec *jsonDecoder) parseMap(dataMap map[string]interface{}) (*yaml.Node, error) {
var yamlMap = &yaml.Node{Kind: yaml.MappingNode} var yamlMap = &yaml.Node{Kind: yaml.MappingNode}
for _, keyValuePair := range data.kv {
for key, value := range dataMap { yamlValue, err := dec.convertToYamlNode(&keyValuePair.V)
yamlValue, err := dec.convertToYamlNode(value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
yamlMap.Content = append(yamlMap.Content, createScalarNode(key, fmt.Sprintf("%v", key)), yamlValue) yamlMap.Content = append(yamlMap.Content, createScalarNode(keyValuePair.K, keyValuePair.K), yamlValue)
} }
return yamlMap, nil return yamlMap, nil
} }
func (dec *jsonDecoder) parseArray(dataArray []interface{}) (*yaml.Node, error) { func (dec *jsonDecoder) parseArray(dataArray []*orderedMap) (*yaml.Node, error) {
var yamlMap = &yaml.Node{Kind: yaml.SequenceNode} var yamlMap = &yaml.Node{Kind: yaml.SequenceNode}

View File

@ -66,8 +66,13 @@ func (o *orderedMap) UnmarshalJSON(data []byte) error {
} }
return nil return nil
case '[': case '[':
var arr []orderedMap var res []*orderedMap
return json.Unmarshal(data, &arr) if err := json.Unmarshal(data, &res); err != nil {
return err
}
o.altVal = res
o.kv = nil
return nil
} }
return json.Unmarshal(data, &o.altVal) return json.Unmarshal(data, &o.altVal)

View File

@ -22,6 +22,13 @@ const sampleNdJson = `{"this": "is a multidoc json file"}
{"a number": 4} {"a number": 4}
` `
const sampleNdJsonKey = `{"a": "first", "b": "next", "ab": "last"}`
const expectedJsonKeysInOrder = `a: first
b: next
ab: last
`
const expectedNdJsonYaml = `this: is a multidoc json file const expectedNdJsonYaml = `this: is a multidoc json file
--- ---
each: each:
@ -157,6 +164,13 @@ var jsonScenarios = []formatScenario{
expected: expectedNdJsonYaml, expected: expectedNdJsonYaml,
scenarioType: "decode-ndjson", scenarioType: "decode-ndjson",
}, },
{
description: "Decode NDJSON, maintain key order",
skipDoc: true,
input: sampleNdJsonKey,
expected: expectedJsonKeysInOrder,
scenarioType: "decode-ndjson",
},
{ {
description: "numbers", description: "numbers",
skipDoc: true, skipDoc: true,
@ -164,6 +178,20 @@ var jsonScenarios = []formatScenario{
expected: "- 3\n- 3\n- 3.1\n- -1\n", expected: "- 3\n- 3\n- 3.1\n- -1\n",
scenarioType: "decode-ndjson", scenarioType: "decode-ndjson",
}, },
{
description: "number single",
skipDoc: true,
input: "3",
expected: "3\n",
scenarioType: "decode-ndjson",
},
{
description: "empty string",
skipDoc: true,
input: `""`,
expected: "\"\"\n",
scenarioType: "decode-ndjson",
},
{ {
description: "strings", description: "strings",
skipDoc: true, skipDoc: true,