diff --git a/pkg/yqlib/decoder_xml.go b/pkg/yqlib/decoder_xml.go index a3dffeb7..d74c579a 100644 --- a/pkg/yqlib/decoder_xml.go +++ b/pkg/yqlib/decoder_xml.go @@ -12,35 +12,16 @@ import ( ) type xmlDecoder struct { - reader io.Reader - readAnything bool - attributePrefix string - directiveName string - procInstPrefix string - contentName string - strictMode bool - keepNamespace bool - useRawToken bool - finished bool - skipDirectives bool - skipProcInst bool + reader io.Reader + readAnything bool + finished bool + prefs xmlPreferences } -func NewXMLDecoder(attributePrefix string, contentName string, strictMode bool, keepNamespace bool, useRawToken bool, skipDirectives bool, skipProcInst bool) Decoder { - if contentName == "" { - contentName = "content" - } +func NewXMLDecoder(prefs xmlPreferences) Decoder { return &xmlDecoder{ - attributePrefix: attributePrefix, - contentName: contentName, - finished: false, - strictMode: strictMode, - keepNamespace: keepNamespace, - useRawToken: useRawToken, - directiveName: "_directive_", - procInstPrefix: "_procInst_", - skipDirectives: skipDirectives, - skipProcInst: skipProcInst, + finished: false, + prefs: prefs, } } @@ -75,7 +56,7 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) { yamlNode := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"} if len(n.Data) > 0 { - label := dec.contentName + label := dec.prefs.ContentName labelNode := createScalarNode(label, label) labelNode.HeadComment = dec.processComment(n.HeadComment) labelNode.FootComment = dec.processComment(n.FootComment) @@ -211,7 +192,7 @@ type element struct { // of the map keys. func (dec *xmlDecoder) decodeXML(root *xmlNode) error { xmlDec := xml.NewDecoder(dec.reader) - xmlDec.Strict = dec.strictMode + xmlDec.Strict = dec.prefs.StrictMode // That will convert the charset if the provided XML is non-UTF-8 xmlDec.CharsetReader = charset.NewReaderLabel @@ -222,7 +203,7 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error { } getToken := func() (xml.Token, error) { - if dec.useRawToken { + if dec.prefs.UseRawToken { return xmlDec.RawToken() } return xmlDec.Token() @@ -250,12 +231,12 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error { // Extract attributes as children for _, a := range se.Attr { - if dec.keepNamespace { + if dec.prefs.KeepNamespace { if a.Name.Space != "" { a.Name.Local = a.Name.Space + ":" + a.Name.Local } } - elem.n.AddChild(dec.attributePrefix+a.Name.Local, &xmlNode{Data: a.Value}) + elem.n.AddChild(dec.prefs.AttributePrefix+a.Name.Local, &xmlNode{Data: a.Value}) } case xml.CharData: // Extract XML data (if any) @@ -289,12 +270,12 @@ func (dec *xmlDecoder) decodeXML(root *xmlNode) error { } case xml.ProcInst: - if !dec.skipProcInst { - elem.n.AddChild(dec.procInstPrefix+se.Target, &xmlNode{Data: string(se.Inst)}) + if !dec.prefs.SkipProcInst { + elem.n.AddChild(dec.prefs.ProcInstPrefix+se.Target, &xmlNode{Data: string(se.Inst)}) } case xml.Directive: - if !dec.skipDirectives { - elem.n.AddChild(dec.directiveName, &xmlNode{Data: string(se)}) + if !dec.prefs.SkipDirectives { + elem.n.AddChild(dec.prefs.DirectiveName, &xmlNode{Data: string(se)}) } } } diff --git a/pkg/yqlib/doc/usage/xml.md b/pkg/yqlib/doc/usage/xml.md index 3d668506..f0d80bf4 100644 --- a/pkg/yqlib/doc/usage/xml.md +++ b/pkg/yqlib/doc/usage/xml.md @@ -30,7 +30,7 @@ yq -p=xml '.' sample.xml ``` will output ```yaml -_procInst_xml: version="1.0" encoding="UTF-8" ++p_xml: version="1.0" encoding="UTF-8" cat: says: meow legs: "4" @@ -55,7 +55,7 @@ yq -p=xml ' (.. | select(tag == "!!str")) |= from_yaml' sample.xml ``` will output ```yaml -_procInst_xml: version="1.0" encoding="UTF-8" ++p_xml: version="1.0" encoding="UTF-8" cat: says: meow legs: 4 @@ -77,7 +77,7 @@ yq -p=xml '.' sample.xml ``` will output ```yaml -_procInst_xml: version="1.0" encoding="UTF-8" ++p_xml: version="1.0" encoding="UTF-8" animal: - cat - goat @@ -99,9 +99,9 @@ yq -p=xml '.' sample.xml ``` will output ```yaml -_procInst_xml: version="1.0" encoding="UTF-8" ++p_xml: version="1.0" encoding="UTF-8" cat: - +legs: "4" + +@legs: "4" legs: "7" ``` @@ -119,14 +119,14 @@ yq -p=xml '.' sample.xml ``` will output ```yaml -_procInst_xml: version="1.0" encoding="UTF-8" ++p_xml: version="1.0" encoding="UTF-8" cat: +content: meow - +legs: "4" + +@legs: "4" ``` ## Parse xml: custom dtd -DTD entities are ignored. +DTD entities are processed as directives. Given a sample.xml file of: ```xml @@ -142,18 +142,45 @@ Given a sample.xml file of: ``` then ```bash -yq -p=xml '.' sample.xml +yq -p=xml -o=xml '.' sample.xml ``` will output -```yaml -_procInst_xml: version="1.0" -_directive_: |- - DOCTYPE root [ - - - ] -root: - item: '&writer;©right;' +```xml + + + +]> + + &writer;&copyright; + +``` + +## Parse xml: skip custom dtd +DTDs are directives, skip over directives to skip DTDs. + +Given a sample.xml file of: +```xml + + + + +]> + + &writer;©right; + +``` +then +```bash +yq -p=xml -o=xml --xml-skip-directives '.' sample.xml +``` +will output +```xml + + + &writer;&copyright; + ``` ## Parse xml: with comments @@ -225,7 +252,7 @@ will output instead of ```xml - + ``` ## Parse xml: keep raw attribute namespace @@ -244,7 +271,7 @@ yq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml will output ```xml - + ``` instead of @@ -293,7 +320,7 @@ Fields with the matching xml-attribute-prefix are assumed to be attributes. Given a sample.yml file of: ```yaml cat: - +name: tiger + +@name: tiger meows: true ``` @@ -314,7 +341,7 @@ Fields with the matching xml-content-name is assumed to be content. Given a sample.yml file of: ```yaml cat: - +name: tiger + +@name: tiger +content: cool ``` @@ -359,11 +386,11 @@ Use the special xml names to add/modify proc instructions and directives. Given a sample.yml file of: ```yaml -_procInst_xml: version="1.0" -_directive_: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" ' ++p_xml: version="1.0" ++directive: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" ' apple: - _procInst_coolioo: version="1.0" - _directive_: 'CATYPE meow purr puss ' + +p_coolioo: version="1.0" + +directive: 'CATYPE meow purr puss ' b: things ``` diff --git a/pkg/yqlib/encoder_xml.go b/pkg/yqlib/encoder_xml.go index ecb6a431..4e3dd151 100644 --- a/pkg/yqlib/encoder_xml.go +++ b/pkg/yqlib/encoder_xml.go @@ -9,24 +9,19 @@ import ( yaml "gopkg.in/yaml.v3" ) -var XMLPreferences = xmlPreferences{AttributePrefix: "+", ContentName: "+content", StrictMode: false, UseRawToken: false} - type xmlEncoder struct { - attributePrefix string - contentName string - indentString string - directiveName string - procInstPrefix string - writer io.Writer + indentString string + writer io.Writer + prefs xmlPreferences } -func NewXMLEncoder(indent int, attributePrefix string, contentName string) Encoder { +func NewXMLEncoder(indent int, prefs xmlPreferences) Encoder { var indentString = "" for index := 0; index < indent; index++ { indentString = indentString + " " } - return &xmlEncoder{attributePrefix, contentName, indentString, "_directive_", "_procInst_", nil} + return &xmlEncoder{indentString, nil, prefs} } func (e *xmlEncoder) CanHandleAliases() bool { @@ -97,8 +92,8 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) er return err } - if strings.HasPrefix(key.Value, e.procInstPrefix) { - name := strings.Replace(key.Value, e.procInstPrefix, "", 1) + if strings.HasPrefix(key.Value, e.prefs.ProcInstPrefix) { + name := strings.Replace(key.Value, e.prefs.ProcInstPrefix, "", 1) procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)} if err := encoder.EncodeToken(procInst); err != nil { return err @@ -106,7 +101,7 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) er if _, err := e.writer.Write([]byte("\n")); err != nil { log.Warning("Unable to write newline, skipping: %w", err) } - } else if key.Value == e.directiveName { + } else if key.Value == e.prefs.DirectiveName { var directive xml.Directive = []byte(value.Value) if err := encoder.EncodeToken(directive); err != nil { return err @@ -205,6 +200,13 @@ func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *yaml.Node, start xm return e.encodeComment(encoder, footComment(node)) } +func (e *xmlEncoder) isAttribute(name string) bool { + return strings.HasPrefix(name, e.prefs.AttributePrefix) && + name != e.prefs.ContentName && + name != e.prefs.DirectiveName && + !strings.HasPrefix(name, e.prefs.ProcInstPrefix) +} + func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error { log.Debug("its a map") @@ -213,9 +215,9 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml. key := node.Content[i] value := node.Content[i+1] - if strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName { + if e.isAttribute(key.Value) { if value.Kind == yaml.ScalarNode { - attributeName := strings.Replace(key.Value, e.attributePrefix, "", 1) + attributeName := strings.Replace(key.Value, e.prefs.AttributePrefix, "", 1) start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: attributeName}, Value: value.Value}) } else { return fmt.Errorf("cannot use %v as attribute, only scalars are supported", value.Tag) @@ -237,24 +239,18 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml. if err != nil { return err } - if strings.HasPrefix(key.Value, e.procInstPrefix) { - name := strings.Replace(key.Value, e.procInstPrefix, "", 1) + if strings.HasPrefix(key.Value, e.prefs.ProcInstPrefix) { + name := strings.Replace(key.Value, e.prefs.ProcInstPrefix, "", 1) procInst := xml.ProcInst{Target: name, Inst: []byte(value.Value)} if err := encoder.EncodeToken(procInst); err != nil { return err } - } else if key.Value == e.directiveName { + } else if key.Value == e.prefs.DirectiveName { var directive xml.Directive = []byte(value.Value) if err := encoder.EncodeToken(directive); err != nil { return err } - } else if !strings.HasPrefix(key.Value, e.attributePrefix) && key.Value != e.contentName { - start := xml.StartElement{Name: xml.Name{Local: key.Value}} - err := e.doEncode(encoder, value, start) - if err != nil { - return err - } - } else if key.Value == e.contentName { + } else if key.Value == e.prefs.ContentName { // directly encode the contents err = e.encodeComment(encoder, headAndLineComment(value)) if err != nil { @@ -269,6 +265,12 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml. if err != nil { return err } + } else if !e.isAttribute(key.Value) { + start := xml.StartElement{Name: xml.Name{Local: key.Value}} + err := e.doEncode(encoder, value, start) + if err != nil { + return err + } } err = e.encodeComment(encoder, footComment(key)) if err != nil { diff --git a/pkg/yqlib/lexer_participle.go b/pkg/yqlib/lexer_participle.go index 170424a7..6cc745cc 100644 --- a/pkg/yqlib/lexer_participle.go +++ b/pkg/yqlib/lexer_participle.go @@ -76,7 +76,7 @@ var participleYqRules = []*participleYqRule{ {"Base64d", `@base64d`, decodeOp(Base64InputFormat), 0}, {"Base64", `@base64`, encodeWithIndent(Base64OutputFormat, 0), 0}, - {"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(XMLPreferences.AttributePrefix, XMLPreferences.ContentName, XMLPreferences.StrictMode, XMLPreferences.KeepNamespace, XMLPreferences.UseRawToken), false), 0}, + {"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(XMLPreferences), false), 0}, {"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0}, diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index e5f42a49..18383383 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -27,8 +27,28 @@ type xmlPreferences struct { StrictMode bool KeepNamespace bool UseRawToken bool + ProcInstPrefix string + DirectiveName string + SkipProcInst bool + SkipDirectives bool } +func NewDefaultXmlPreferences() xmlPreferences { + return xmlPreferences{ + AttributePrefix: "+@", + ContentName: "+content", + StrictMode: false, + KeepNamespace: true, + UseRawToken: false, + ProcInstPrefix: "+p_", + DirectiveName: "+directive", + SkipProcInst: false, + SkipDirectives: false, + } +} + +var XMLPreferences = NewDefaultXmlPreferences() + var log = logging.MustGetLogger("yq-lib") var PrettyPrintExp = `(... | (select(tag != "!!str"), select(tag == "!!str") | select(test("(?i)^(y|yes|n|no|on|off)$") | not)) ) style=""` diff --git a/pkg/yqlib/operator_encoder_decoder.go b/pkg/yqlib/operator_encoder_decoder.go index afb321ef..e8b8a963 100644 --- a/pkg/yqlib/operator_encoder_decoder.go +++ b/pkg/yqlib/operator_encoder_decoder.go @@ -23,7 +23,7 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder { case YamlOutputFormat: return NewYamlEncoder(indent, false, true, true) case XMLOutputFormat: - return NewXMLEncoder(indent, XMLPreferences.AttributePrefix, XMLPreferences.ContentName) + return NewXMLEncoder(indent, XMLPreferences) case Base64OutputFormat: return NewBase64Encoder() } @@ -104,12 +104,7 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre case YamlInputFormat: decoder = NewYamlDecoder() case XMLInputFormat: - decoder = NewXMLDecoder( - XMLPreferences.AttributePrefix, - XMLPreferences.ContentName, - XMLPreferences.StrictMode, - XMLPreferences.KeepNamespace, - XMLPreferences.UseRawToken) + decoder = NewXMLDecoder(XMLPreferences) case Base64InputFormat: decoder = NewBase64Decoder() case PropertiesInputFormat: diff --git a/pkg/yqlib/xml_test.go b/pkg/yqlib/xml_test.go index 34b85b15..0c69a24e 100644 --- a/pkg/yqlib/xml_test.go +++ b/pkg/yqlib/xml_test.go @@ -58,7 +58,7 @@ cat: d: # in d before z: - +sweet: cool + +@sweet: cool # in d after # in y after # in_cat_after @@ -98,11 +98,11 @@ cat: d: - # in d before z: - +sweet: cool + +@sweet: cool # in d after - # in d2 before z: - +sweet: cool2 + +@sweet: cool2 # in d2 after # in y after # in_cat_after @@ -159,18 +159,18 @@ const inputXMLWithNamespacedAttr = ` ` -const expectedYAMLWithNamespacedAttr = `_procInst_xml: version="1.0" +const expectedYAMLWithNamespacedAttr = `+p_xml: version="1.0" map: - +xmlns: some-namespace - +xmlns:xsi: some-instance - +some-instance:schemaLocation: some-url + +@xmlns: some-namespace + +@xmlns:xsi: some-instance + +@some-instance:schemaLocation: some-url ` -const expectedYAMLWithRawNamespacedAttr = `_procInst_xml: version="1.0" +const expectedYAMLWithRawNamespacedAttr = `+p_xml: version="1.0" map: - +xmlns: some-namespace - +xmlns:xsi: some-instance - +xsi:schemaLocation: some-url + +@xmlns: some-namespace + +@xmlns:xsi: some-instance + +@xsi:schemaLocation: some-url ` const xmlWithCustomDtd = ` @@ -183,18 +183,19 @@ const xmlWithCustomDtd = ` &writer;©right; ` -const expectedDtd = `_procInst_xml: version="1.0" -_directive_: |- - DOCTYPE root [ - - - ] -root: - item: '&writer;©right;' +const expectedDtd = ` + + +]> + + &writer;&copyright; + ` -const expectedSkippedDtd = `root: - item: '&writer;©right;' +const expectedSkippedDtd = ` + &writer;©right; + ` const xmlWithProcInstAndDirectives = ` @@ -206,11 +207,11 @@ const xmlWithProcInstAndDirectives = ` ` -const yamlWithProcInstAndDirectives = `_procInst_xml: version="1.0" -_directive_: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" ' +const yamlWithProcInstAndDirectives = `+p_xml: version="1.0" ++directive: 'DOCTYPE config SYSTEM "/etc/iwatch/iwatch.dtd" ' apple: - _procInst_coolioo: version="1.0" - _directive_: 'CATYPE meow purr puss ' + +p_coolioo: version="1.0" + +directive: 'CATYPE meow purr puss ' b: things ` @@ -258,13 +259,14 @@ var xmlScenarios = []formatScenario{ subdescription: "DTD entities are processed as directives.", input: xmlWithCustomDtd, expected: expectedDtd, + scenarioType: "roundtrip", }, { - description: "Parse xml: custom dtd", - subdescription: "DTD entities are processed as directives.", + description: "Parse xml: skip custom dtd", + subdescription: "DTDs are directives, skip over directives to skip DTDs.", input: xmlWithCustomDtd, expected: expectedSkippedDtd, - scenarioType: "c", + scenarioType: "roundtrip-skip-directives", }, { description: "Parse xml: with comments", @@ -360,20 +362,21 @@ var xmlScenarios = []formatScenario{ { description: "Encode xml: attributes", subdescription: "Fields with the matching xml-attribute-prefix are assumed to be attributes.", - input: "cat:\n +name: tiger\n meows: true\n", + input: "cat:\n +@name: tiger\n meows: true\n", expected: "\n true\n\n", scenarioType: "encode", }, { + description: "double prefix", skipDoc: true, - input: "cat:\n ++name: tiger\n meows: true\n", - expected: "\n true\n\n", + input: "cat:\n +@+@name: tiger\n meows: true\n", + expected: "\n true\n\n", scenarioType: "encode", }, { 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", + input: "cat:\n +@name: tiger\n +content: cool\n", expected: "cool\n", scenarioType: "encode", }, @@ -410,17 +413,23 @@ var xmlScenarios = []formatScenario{ func testXMLScenario(t *testing.T, s formatScenario) { switch s.scenarioType { case "", "decode": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewYamlEncoder(4, false, true, true)), s.description) + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewYamlEncoder(4, false, true, true)), s.description) case "encode": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, "+", "+content")), s.description) + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, XMLPreferences)), s.description) case "roundtrip": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content")), s.description) + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewXMLEncoder(2, XMLPreferences)), s.description) case "decode-keep-ns": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, false, false, false), NewYamlEncoder(2, false, true, true)), s.description) + prefs := NewDefaultXmlPreferences() + prefs.KeepNamespace = true + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewYamlEncoder(2, false, true, true)), s.description) case "decode-raw-token": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, false, false), NewYamlEncoder(2, false, true, true)), s.description) - case "encode-": - test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, true, true), NewYamlEncoder(2, false, true, true)), s.description) + prefs := NewDefaultXmlPreferences() + prefs.UseRawToken = true + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewYamlEncoder(2, false, true, true)), s.description) + case "roundtrip-skip-directives": + prefs := NewDefaultXmlPreferences() + prefs.SkipDirectives = true + test.AssertResultWithContext(t, s.expected, processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)), s.description) default: panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType)) } @@ -443,6 +452,8 @@ func documentXMLScenario(t *testing.T, w *bufio.Writer, i interface{}) { documentXMLDecodeKeepNsScenario(w, s) case "decode-raw-token": documentXMLDecodeKeepNsRawTokenScenario(w, s) + case "roundtrip-skip-directives": + documentXMLSkipDirectrivesScenario(w, s) default: panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType)) @@ -468,7 +479,7 @@ func documentXMLDecodeScenario(w *bufio.Writer, s formatScenario) { writeOrPanic(w, fmt.Sprintf("```bash\nyq -p=xml '%v' sample.xml\n```\n", expression)) writeOrPanic(w, "will output\n") - writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false), NewYamlEncoder(2, false, true, true)))) + writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewYamlEncoder(2, false, true, true)))) } func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) { @@ -485,11 +496,14 @@ func documentXMLDecodeKeepNsScenario(w *bufio.Writer, s formatScenario) { writeOrPanic(w, "then\n") writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace '.' sample.xml\n```\n") writeOrPanic(w, "will output\n") + prefs := NewDefaultXmlPreferences() + prefs.KeepNamespace = true + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)))) - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, false, false, false), NewXMLEncoder(2, "+", "+content")))) - + prefsWithout := NewDefaultXmlPreferences() + prefs.KeepNamespace = false writeOrPanic(w, "instead of\n") - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content")))) + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(2, prefsWithout)))) } func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) { @@ -507,10 +521,16 @@ func documentXMLDecodeKeepNsRawTokenScenario(w *bufio.Writer, s formatScenario) writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-keep-namespace --xml-raw-token '.' sample.xml\n```\n") writeOrPanic(w, "will output\n") - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, true, true, false, false), NewXMLEncoder(2, "+", "+content")))) + prefs := NewDefaultXmlPreferences() + prefs.KeepNamespace = true + + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)))) + + prefsWithout := NewDefaultXmlPreferences() + prefsWithout.KeepNamespace = false writeOrPanic(w, "instead of\n") - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content")))) + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefsWithout), NewXMLEncoder(2, prefsWithout)))) } func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) { @@ -528,7 +548,7 @@ func documentXMLEncodeScenario(w *bufio.Writer, s formatScenario) { writeOrPanic(w, "```bash\nyq -o=xml '.' sample.yml\n```\n") writeOrPanic(w, "will output\n") - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, "+", "+content")))) + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewYamlDecoder(), NewXMLEncoder(2, XMLPreferences)))) } func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) { @@ -546,7 +566,27 @@ func documentXMLRoundTripScenario(w *bufio.Writer, s formatScenario) { writeOrPanic(w, "```bash\nyq -p=xml -o=xml '.' sample.xml\n```\n") writeOrPanic(w, "will output\n") - writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder("+", "+content", false, false, false, false, false), NewXMLEncoder(2, "+", "+content")))) + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(XMLPreferences), NewXMLEncoder(2, XMLPreferences)))) +} + +func documentXMLSkipDirectrivesScenario(w *bufio.Writer, s formatScenario) { + writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) + + if s.subdescription != "" { + writeOrPanic(w, s.subdescription) + writeOrPanic(w, "\n\n") + } + + writeOrPanic(w, "Given a sample.xml file of:\n") + writeOrPanic(w, fmt.Sprintf("```xml\n%v\n```\n", s.input)) + + writeOrPanic(w, "then\n") + writeOrPanic(w, "```bash\nyq -p=xml -o=xml --xml-skip-directives '.' sample.xml\n```\n") + writeOrPanic(w, "will output\n") + prefs := NewDefaultXmlPreferences() + prefs.SkipDirectives = true + + writeOrPanic(w, fmt.Sprintf("```xml\n%v```\n\n", processFormatScenario(s, NewXMLDecoder(prefs), NewXMLEncoder(2, prefs)))) } func TestXMLScenarios(t *testing.T) {