From 6bcbd873a6f30d9e14b6f9568c6218f40a963a84 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Tue, 21 Dec 2021 16:19:27 +1100 Subject: [PATCH] wip --- examples/mike.xml | 8 ++++---- pkg/yqlib/doc/operators/encode-decode.md | 1 + pkg/yqlib/doc/usage/xml.md | 19 +++++++++++++++++++ pkg/yqlib/encoder_xml.go | 16 ++++++++++++---- pkg/yqlib/printer.go | 5 +++++ pkg/yqlib/xml_test.go | 13 ++++++++++--- 6 files changed, 51 insertions(+), 11 deletions(-) diff --git a/examples/mike.xml b/examples/mike.xml index e64b598b..99a1afef 100644 --- a/examples/mike.xml +++ b/examples/mike.xml @@ -1,4 +1,4 @@ - - 3f - meow:as - true \ No newline at end of file + +3f +meow:as +true \ No newline at end of file diff --git a/pkg/yqlib/doc/operators/encode-decode.md b/pkg/yqlib/doc/operators/encode-decode.md index d77aa917..1def115e 100644 --- a/pkg/yqlib/doc/operators/encode-decode.md +++ b/pkg/yqlib/doc/operators/encode-decode.md @@ -14,6 +14,7 @@ These operators are useful to process yaml documents that have stringified embed | Properties | | to_props/@props | | CSV | | to_csv/@csv | | TSV | | to_tsv/@tsv | +| XML | from_xml | | CSV and TSV format both accept either a single array or scalars (representing a single row), or an array of array of scalars (representing multiple rows). diff --git a/pkg/yqlib/doc/usage/xml.md b/pkg/yqlib/doc/usage/xml.md index 1215047c..7c37ce25 100644 --- a/pkg/yqlib/doc/usage/xml.md +++ b/pkg/yqlib/doc/usage/xml.md @@ -141,3 +141,22 @@ will output ``` +## Encode xml: attributes with content +Fields with the matching xml-content-name is assumed to be content. + +Given a sample.yml file of: +```yaml +cat: + +name: tiger + +content: cool + +``` +then +```bash +yq e -o=xml '.' sample.yml +``` +will output +```xml +cool +``` + diff --git a/pkg/yqlib/encoder_xml.go b/pkg/yqlib/encoder_xml.go index b5fe832d..2ebcd577 100644 --- a/pkg/yqlib/encoder_xml.go +++ b/pkg/yqlib/encoder_xml.go @@ -12,9 +12,10 @@ import ( type xmlEncoder struct { xmlEncoder *xml.Encoder attributePrefix string + contentName string } -func NewXmlEncoder(writer io.Writer, indent int, attributePrefix string) Encoder { +func NewXmlEncoder(writer io.Writer, indent int, attributePrefix string, contentName string) Encoder { encoder := xml.NewEncoder(writer) var indentString = "" @@ -22,7 +23,7 @@ func NewXmlEncoder(writer io.Writer, indent int, attributePrefix string) Encoder indentString = indentString + " " } encoder.Indent("", indentString) - return &xmlEncoder{encoder, attributePrefix} + return &xmlEncoder{encoder, attributePrefix, contentName} } func (e *xmlEncoder) Encode(node *yaml.Node) error { switch node.Kind { @@ -92,7 +93,7 @@ func (e *xmlEncoder) encodeMap(node *yaml.Node, start xml.StartElement) error { key := node.Content[i] value := node.Content[i+1] - if strings.HasPrefix(key.Value, e.attributePrefix) { + if strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName { if value.Kind == yaml.ScalarNode { attributeName := strings.Replace(key.Value, e.attributePrefix, "", 1) start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: attributeName}, Value: value.Value}) @@ -112,12 +113,19 @@ func (e *xmlEncoder) encodeMap(node *yaml.Node, start xml.StartElement) error { key := node.Content[i] value := node.Content[i+1] - if !strings.HasPrefix(key.Value, e.attributePrefix) { + if !strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName { start := xml.StartElement{Name: xml.Name{Local: key.Value}} err := e.doEncode(value, start) if err != nil { return err } + } else if key.Value == e.contentName { + // directly encode the contents + var charData xml.CharData = []byte(value.Value) + err = e.xmlEncoder.EncodeToken(charData) + if err != nil { + return err + } } } diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 20446bbd..ab5d05d4 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -25,6 +25,7 @@ const ( PropsOutputFormat CsvOutputFormat TsvOutputFormat + XmlOutputFormat ) func OutputFormatFromString(format string) (PrinterOutputFormat, error) { @@ -39,6 +40,8 @@ func OutputFormatFromString(format string) (PrinterOutputFormat, error) { return CsvOutputFormat, nil case "tsv", "t": return TsvOutputFormat, nil + case "xml", "x": + return XmlOutputFormat, nil default: return 0, fmt.Errorf("unknown format '%v' please use [yaml|json|props|csv|tsv]", format) } @@ -104,6 +107,8 @@ func (p *resultsPrinter) printNode(node *yaml.Node, writer io.Writer) error { encoder = NewCsvEncoder(writer, '\t') case YamlOutputFormat: encoder = NewYamlEncoder(writer, p.indent, p.colorsEnabled) + case XmlOutputFormat: + encoder = NewXmlEncoder(writer, p.indent, "+", "+content") } return encoder.Encode(node) diff --git a/pkg/yqlib/xml_test.go b/pkg/yqlib/xml_test.go index 04302e68..f3b06562 100644 --- a/pkg/yqlib/xml_test.go +++ b/pkg/yqlib/xml_test.go @@ -28,7 +28,7 @@ func yamlToXml(sampleYaml string, indent int) string { var output bytes.Buffer writer := bufio.NewWriter(&output) - var encoder = NewXmlEncoder(writer, indent, "+") + var encoder = NewXmlEncoder(writer, indent, "+", "+content") inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0, NewYamlDecoder()) if err != nil { panic(err) @@ -101,6 +101,13 @@ var xmlScenarios = []xmlScenario{ expected: "\n true\n", encodeScenario: true, }, + { + description: "Encode xml: attributes with content", + subdescription: "Fields with the matching xml-content-name is assumed to be content.", + input: "cat:\n +name: tiger\n +content: cool\n", + expected: "cool", + encodeScenario: true, + }, } //encode @@ -121,7 +128,7 @@ func documentXmlScenario(t *testing.T, w *bufio.Writer, i interface{}) { return } if s.encodeScenario { - documentXmlEncodeScenario(t, w, s) + documentXmlEncodeScenario(w, s) } else { documentXmlDecodeScenario(t, w, s) } @@ -157,7 +164,7 @@ func documentXmlDecodeScenario(t *testing.T, w *bufio.Writer, s xmlScenario) { writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", output.String())) } -func documentXmlEncodeScenario(t *testing.T, w *bufio.Writer, s xmlScenario) { +func documentXmlEncodeScenario(w *bufio.Writer, s xmlScenario) { writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) if s.subdescription != "" {