Unwrap scalar now works for JSON encoding when explicitly set #1409

This commit is contained in:
Mike Farah 2022-11-10 19:21:18 +11:00
parent fae1dbf0be
commit 1d35134310
10 changed files with 147 additions and 19 deletions

View File

@ -48,6 +48,55 @@ EOM
assertEquals "$expected" "$X" assertEquals "$expected" "$X"
} }
testOutputYamlRawDefault() {
cat >test.yml <<EOL
a: "cat"
EOL
X=$(./yq e '.a' test.yml)
assertEquals "cat" "$X"
X=$(./yq ea '.a' test.yml)
assertEquals "cat" "$X"
}
testOutputYamlRawOff() {
cat >test.yml <<EOL
a: "cat"
EOL
X=$(./yq e -r=false '.a' test.yml)
assertEquals "\"cat\"" "$X"
X=$(./yq ea -r=false '.a' test.yml)
assertEquals "\"cat\"" "$X"
}
testOutputJsonRaw() {
cat >test.yml <<EOL
a: cat
EOL
X=$(./yq e -r --output-format=json '.a' test.yml)
assertEquals "cat" "$X"
X=$(./yq ea -r --output-format=json '.a' test.yml)
assertEquals "cat" "$X"
}
testOutputJsonDefault() {
cat >test.yml <<EOL
a: cat
EOL
X=$(./yq e --output-format=json '.a' test.yml)
assertEquals "\"cat\"" "$X"
X=$(./yq ea --output-format=json '.a' test.yml)
assertEquals "\"cat\"" "$X"
}
testOutputJsonShort() { testOutputJsonShort() {
cat >test.yml <<EOL cat >test.yml <<EOL
a: {b: ["cat"]} a: {b: ["cat"]}
@ -72,11 +121,11 @@ EOM
testOutputProperties() { testOutputProperties() {
cat >test.yml <<EOL cat >test.yml <<EOL
a: {b: {c: ["cat"]}} a: {b: {c: ["cat cat"]}}
EOL EOL
read -r -d '' expected << EOM read -r -d '' expected << EOM
a.b.c.0 = cat a.b.c.0 = cat cat
EOM EOM
X=$(./yq e --output-format=props test.yml) X=$(./yq e --output-format=props test.yml)
@ -86,13 +135,30 @@ EOM
assertEquals "$expected" "$X" assertEquals "$expected" "$X"
} }
testOutputPropertiesShort() { testOutputPropertiesDontUnwrap() {
cat >test.yml <<EOL cat >test.yml <<EOL
a: {b: {c: ["cat"]}} a: {b: {c: ["cat cat"]}}
EOL EOL
read -r -d '' expected << EOM read -r -d '' expected << EOM
a.b.c.0 = cat a.b.c.0 = "cat cat"
EOM
X=$(./yq e -r=false --output-format=props test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -r=false --output-format=props test.yml)
assertEquals "$expected" "$X"
}
testOutputPropertiesShort() {
cat >test.yml <<EOL
a: {b: {c: ["cat cat"]}}
EOL
read -r -d '' expected << EOM
a.b.c.0 = cat cat
EOM EOM
X=$(./yq e -o=p test.yml) X=$(./yq e -o=p test.yml)

View File

@ -1,6 +1,53 @@
package cmd package cmd
var unwrapScalar = true import (
"strconv"
"github.com/spf13/pflag"
)
type boolFlag interface {
pflag.Value
IsExplicitySet() bool
IsSet() bool
}
type unwrapScalarFlagStrc struct {
explicitySet bool
value bool
}
func newFlag() boolFlag {
return &unwrapScalarFlagStrc{value: true}
}
func (f *unwrapScalarFlagStrc) IsExplicitySet() bool {
return f.explicitySet
}
func (f *unwrapScalarFlagStrc) IsSet() bool {
return f.value
}
func (f *unwrapScalarFlagStrc) String() string {
return strconv.FormatBool(f.value)
}
func (f *unwrapScalarFlagStrc) Set(value string) error {
v, err := strconv.ParseBool(value)
f.value = v
f.explicitySet = true
return err
}
func (*unwrapScalarFlagStrc) Type() string {
return "bool"
}
var unwrapScalarFlag = newFlag()
var unwrapScalar = false
var writeInplace = false var writeInplace = false
var outputToJSON = false var outputToJSON = false

View File

@ -60,6 +60,14 @@ yq -P sample.json
"`+` please set that value explicityly with --xml-attribute-prefix.") "`+` please set that value explicityly with --xml-attribute-prefix.")
} }
if outputFormat == "y" || outputFormat == "yaml" ||
outputFormat == "p" || outputFormat == "props" {
unwrapScalar = true
}
if unwrapScalarFlag.IsExplicitySet() {
unwrapScalar = unwrapScalarFlag.IsSet()
}
//copy preference form global setting //copy preference form global setting
yqlib.ConfiguredYamlPreferences.UnwrapScalar = unwrapScalar yqlib.ConfiguredYamlPreferences.UnwrapScalar = unwrapScalar
@ -94,7 +102,9 @@ yq -P sample.json
rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output") rootCmd.PersistentFlags().IntVarP(&indent, "indent", "I", 2, "sets indent level for output")
rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit") rootCmd.Flags().BoolVarP(&version, "version", "V", false, "Print version information and quit")
rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the file inplace of first file given.") rootCmd.PersistentFlags().BoolVarP(&writeInplace, "inplace", "i", false, "update the file inplace of first file given.")
rootCmd.PersistentFlags().BoolVarP(&unwrapScalar, "unwrapScalar", "r", true, "unwrap scalar, print the value with no quotes, colors or comments") rootCmd.PersistentFlags().VarP(unwrapScalarFlag, "unwrapScalar", "r", "unwrap scalar, print the value with no quotes, colors or comments. Defaults to true for yaml")
rootCmd.PersistentFlags().Lookup("unwrapScalar").NoOptDefVal = "true"
rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print, shorthand for '... style = \"\"'") rootCmd.PersistentFlags().BoolVarP(&prettyPrint, "prettyPrint", "P", false, "pretty print, shorthand for '... style = \"\"'")
rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned") rootCmd.PersistentFlags().BoolVarP(&exitStatus, "exit-status", "e", false, "set exit status if there are no matches or null or false is returned")

View File

@ -98,7 +98,7 @@ func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) (yq
func configureEncoder(format yqlib.PrinterOutputFormat) yqlib.Encoder { func configureEncoder(format yqlib.PrinterOutputFormat) yqlib.Encoder {
switch format { switch format {
case yqlib.JSONOutputFormat: case yqlib.JSONOutputFormat:
return yqlib.NewJSONEncoder(indent, colorsEnabled) return yqlib.NewJSONEncoder(indent, colorsEnabled, unwrapScalar)
case yqlib.PropsOutputFormat: case yqlib.PropsOutputFormat:
return yqlib.NewPropertiesEncoder(unwrapScalar) return yqlib.NewPropertiesEncoder(unwrapScalar)
case yqlib.CSVOutputFormat: case yqlib.CSVOutputFormat:

2
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/magiconair/properties v1.8.6 github.com/magiconair/properties v1.8.6
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
golang.org/x/net v0.0.0-20220708220712-1185a9018129 golang.org/x/net v0.0.0-20220708220712-1185a9018129
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@ -22,7 +23,6 @@ require (
github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect

View File

@ -11,6 +11,7 @@ import (
type jsonEncoder struct { type jsonEncoder struct {
indentString string indentString string
colorise bool colorise bool
UnwrapScalar bool
} }
func mapKeysToStrings(node *yaml.Node) { func mapKeysToStrings(node *yaml.Node) {
@ -28,14 +29,14 @@ func mapKeysToStrings(node *yaml.Node) {
} }
} }
func NewJSONEncoder(indent int, colorise bool) Encoder { func NewJSONEncoder(indent int, colorise bool, unwrapScalar bool) Encoder {
var indentString = "" var indentString = ""
for index := 0; index < indent; index++ { for index := 0; index < indent; index++ {
indentString = indentString + " " indentString = indentString + " "
} }
return &jsonEncoder{indentString, colorise} return &jsonEncoder{indentString, colorise, unwrapScalar}
} }
func (je *jsonEncoder) CanHandleAliases() bool { func (je *jsonEncoder) CanHandleAliases() bool {
@ -52,6 +53,10 @@ func (je *jsonEncoder) PrintLeadingContent(writer io.Writer, content string) err
func (je *jsonEncoder) Encode(writer io.Writer, node *yaml.Node) error { func (je *jsonEncoder) Encode(writer io.Writer, node *yaml.Node) error {
if node.Kind == yaml.ScalarNode && je.UnwrapScalar {
return writeString(writer, node.Value+"\n")
}
destination := writer destination := writer
tempBuffer := bytes.NewBuffer(nil) tempBuffer := bytes.NewBuffer(nil)
if je.colorise { if je.colorise {

View File

@ -13,7 +13,7 @@ func yamlToJSON(sampleYaml string, indent int) string {
var output bytes.Buffer var output bytes.Buffer
writer := bufio.NewWriter(&output) writer := bufio.NewWriter(&output)
var jsonEncoder = NewJSONEncoder(indent, false) var jsonEncoder = NewJSONEncoder(indent, false, false)
inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences))
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -237,7 +237,7 @@ func documentRoundtripNdJsonScenario(w *bufio.Writer, s formatScenario, indent i
writeOrPanic(w, "will output\n") writeOrPanic(w, "will output\n")
writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(indent, false)))) writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(indent, false, false))))
} }
func documentDecodeNdJsonScenario(w *bufio.Writer, s formatScenario) { func documentDecodeNdJsonScenario(w *bufio.Writer, s formatScenario) {
@ -293,16 +293,16 @@ func decodeJSON(t *testing.T, jsonString string) *CandidateNode {
func testJSONScenario(t *testing.T, s formatScenario) { func testJSONScenario(t *testing.T, s formatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "encode", "decode": case "encode", "decode":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewJSONEncoder(s.indent, false)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewJSONEncoder(s.indent, false, false)), s.description)
case "": case "":
var actual = resultToString(t, decodeJSON(t, s.input)) var actual = resultToString(t, decodeJSON(t, s.input))
test.AssertResultWithContext(t, s.expected, actual, s.description) test.AssertResultWithContext(t, s.expected, actual, s.description)
case "decode-ndjson": case "decode-ndjson":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description)
case "roundtrip-ndjson": case "roundtrip-ndjson":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(0, false)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(0, false, false)), s.description)
case "roundtrip-multi": case "roundtrip-multi":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(2, false)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewJSONDecoder(), NewJSONEncoder(2, false, false)), s.description)
default: default:
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType)) panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
@ -385,7 +385,7 @@ func documentJSONEncodeScenario(w *bufio.Writer, s formatScenario) {
} }
writeOrPanic(w, "will output\n") writeOrPanic(w, "will output\n")
writeOrPanic(w, fmt.Sprintf("```json\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewJSONEncoder(s.indent, false)))) writeOrPanic(w, fmt.Sprintf("```json\n%v```\n\n", mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewJSONEncoder(s.indent, false, false))))
} }
func TestJSONScenarios(t *testing.T) { func TestJSONScenarios(t *testing.T) {

View File

@ -13,7 +13,7 @@ import (
func configureEncoder(format PrinterOutputFormat, indent int) Encoder { func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
switch format { switch format {
case JSONOutputFormat: case JSONOutputFormat:
return NewJSONEncoder(indent, false) return NewJSONEncoder(indent, false, false)
case PropsOutputFormat: case PropsOutputFormat:
return NewPropertiesEncoder(true) return NewPropertiesEncoder(true)
case CSVOutputFormat: case CSVOutputFormat:

View File

@ -314,7 +314,7 @@ func TestPrinterMultipleDocsJson(t *testing.T) {
var writer = bufio.NewWriter(&output) var writer = bufio.NewWriter(&output)
// note printDocSeparators is true, it should still not print document separators // note printDocSeparators is true, it should still not print document separators
// when outputing JSON. // when outputing JSON.
printer := NewPrinter(NewJSONEncoder(0, false), NewSinglePrinterWriter(writer)) printer := NewPrinter(NewJSONEncoder(0, false, false), NewSinglePrinterWriter(writer))
inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences))
if err != nil { if err != nil {