diff --git a/pkg/yqlib/decoder_xml.go b/pkg/yqlib/decoder_xml.go index 3c0e5451..bdb56a49 100644 --- a/pkg/yqlib/decoder_xml.go +++ b/pkg/yqlib/decoder_xml.go @@ -63,14 +63,14 @@ func (dec *xmlDecoder) createSequence(nodes []*xmlNode) (*yaml.Node, error) { func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) { log.Debug("createMap: headC: %v, footC: %v", n.HeadComment, n.FootComment) - yamlNode := &yaml.Node{Kind: yaml.MappingNode, HeadComment: n.HeadComment} + yamlNode := &yaml.Node{Kind: yaml.MappingNode, HeadComment: n.HeadComment, FootComment: n.FootComment} if len(n.Data) > 0 { label := dec.contentPrefix yamlNode.Content = append(yamlNode.Content, createScalarNode(label, label), createScalarNode(n.Data, n.Data)) } - for i, keyValuePair := range n.Children { + for _, keyValuePair := range n.Children { label := keyValuePair.K children := keyValuePair.V labelNode := createScalarNode(label, label) @@ -88,9 +88,6 @@ func (dec *xmlDecoder) createMap(n *xmlNode) (*yaml.Node, error) { if err != nil { return nil, err } - if i == len(n.Children)-1 { - valueNode.FootComment = n.FootComment - } } yamlNode.Content = append(yamlNode.Content, labelNode, valueNode) } @@ -105,6 +102,7 @@ func (dec *xmlDecoder) convertToYamlNode(n *xmlNode) (*yaml.Node, error) { scalar := createScalarNode(n.Data, n.Data) log.Debug("scalar headC: %v, footC: %v", n.HeadComment, n.FootComment) scalar.LineComment = n.HeadComment + scalar.FootComment = n.FootComment return scalar, nil } @@ -221,10 +219,21 @@ func (dec *xmlDecoder) decodeXml(root *xmlNode) error { elem = elem.parent case xml.Comment: - commentStr := trimNonGraphic(string(xml.CharData(se))) + commentStr := string(xml.CharData(se)) if elem.state == "started" { log.Debug("got a foot comment for %v: %v", elem.label, commentStr) - elem.n.FootComment = commentStr + // elem.n.FootComment = elem.n.FootComment + commentStr + // put the comment on the foot of the last child + if len(elem.n.Children) > 0 { + + child := elem.n.Children[len(elem.n.Children)-1] + log.Debug("putting it here: %v", child.K) + child.V[0].FootComment = child.V[0].FootComment + commentStr + } else { + log.Debug("putting it on the element") + elem.n.FootComment = elem.n.FootComment + commentStr + } + } else { log.Debug("got a head comment for %v: %v", elem.label, commentStr) elem.n.HeadComment = joinFilter([]string{elem.n.HeadComment, commentStr}) diff --git a/pkg/yqlib/doc/usage/xml.md b/pkg/yqlib/doc/usage/xml.md index f3a31666..2f424cba 100644 --- a/pkg/yqlib/doc/usage/xml.md +++ b/pkg/yqlib/doc/usage/xml.md @@ -22,224 +22,6 @@ XML nodes that have attributes then plain content, e.g: The content of the node will be set as a field in the map with the key "+content". Use the `--xml-content-name` flag to change this. -## Parse xml: simple -Given a sample.xml file of: -```xml - -meow -``` -then -```bash -yq e -p=xml '.' sample.xml -``` -will output -```yaml -cat: meow -``` - -## Parse xml: array -Consecutive nodes with identical xml names are assumed to be arrays. - -Given a sample.xml file of: -```xml - -1 -2 -``` -then -```bash -yq e -p=xml '.' sample.xml -``` -will output -```yaml -animal: - - "1" - - "2" -``` - -## Parse xml: attributes -Attributes are converted to fields, with the attribute prefix. - -Given a sample.xml file of: -```xml - - - 7 - -``` -then -```bash -yq e -p=xml '.' sample.xml -``` -will output -```yaml -cat: - +legs: "4" - legs: "7" -``` - -## Parse xml: attributes with content -Content is added as a field, using the content name - -Given a sample.xml file of: -```xml - -meow -``` -then -```bash -yq e -p=xml '.' sample.xml -``` -will output -```yaml -cat: - +content: meow - +legs: "4" -``` - -## Parse xml: with comments -A best attempt is made to preserve comments. - -Given a sample.xml file of: -```xml - - - - - 3 - - - 4 - - - - - - -``` -then -```bash -yq e -p=xml '.' sample.xml -``` -will output -```yaml -# before cat -cat: - # in cat before - x: "3" # multi - # line comment - # for x - y: - # in y before - d: "4" # in d before in d after - # in y after - -# after cat -``` - -## Encode xml: simple -Given a sample.yml file of: -```yaml -cat: purrs -``` -then -```bash -yq e -o=xml '.' sample.yml -``` -will output -```xml -purrs -``` - -## Encode xml: array -Given a sample.yml file of: -```yaml -pets: - cat: - - purrs - - meows -``` -then -```bash -yq e -o=xml '.' sample.yml -``` -will output -```xml - - purrs - meows - -``` - -## Encode xml: attributes -Fields with the matching xml-attribute-prefix are assumed to be attributes. - -Given a sample.yml file of: -```yaml -cat: - +name: tiger - meows: true - -``` -then -```bash -yq e -o=xml '.' sample.yml -``` -will output -```xml - - true - -``` - -## 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 -``` - -## Encode xml: comments -A best attempt is made to copy comments to xml. - -Given a sample.yml file of: -```yaml -# above_cat -cat: # inline_cat - # above_array - array: # inline_array - - val1 # inline_val1 - # above_val2 - - val2 # inline_val2 -# below_cat - -``` -then -```bash -yq e -o=xml '.' sample.yml -``` -will output -```xml - - val1 - val2 - -``` - ## Round trip: with comments A best effort is made, but comment positions and white space are not preserved perfectly. @@ -252,6 +34,7 @@ Given a sample.xml file of: 3 + 4 @@ -268,17 +51,19 @@ yq e -p=xml '.' sample.xml ``` will output ```yaml -# before cat +# before cat cat: - # in cat before - x: "3" # multi + # in cat before + x: "3" # multi # line comment - # for x - y: - # in y before - d: "4" # in d before in d after - # in y after + # for x + # before y -# after cat + y: + # in y before + d: "4" # in d before in d after + # in y after + +# after cat ``` diff --git a/pkg/yqlib/encoder_xml.go b/pkg/yqlib/encoder_xml.go index 329c57fa..12b3e547 100644 --- a/pkg/yqlib/encoder_xml.go +++ b/pkg/yqlib/encoder_xml.go @@ -49,11 +49,12 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *yaml.Node) error { return err } case yaml.DocumentNode: + log.Debugf("ENCODING DOCUMENT NODE") err := e.encodeComment(encoder, headAndLineComment(node)) if err != nil { return err } - // this used to call encode... + log.Debugf("OK NOW THE ACTUAL") err = e.encodeTopLevelMap(encoder, unwrapDoc(node)) if err != nil { return err @@ -78,16 +79,23 @@ func (e *xmlEncoder) Encode(writer io.Writer, node *yaml.Node) error { } func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) error { + err := e.encodeComment(encoder, headAndLineComment(node)) + if err != nil { + return err + } for i := 0; i < len(node.Content); i += 2 { key := node.Content[i] value := node.Content[i+1] start := xml.StartElement{Name: xml.Name{Local: key.Value}} + log.Debugf("comments of key %v", key.Value) err := e.encodeComment(encoder, headAndLineComment(key)) if err != nil { return err } + log.Debugf("recursing") + err = e.doEncode(encoder, value, start) if err != nil { return err @@ -97,7 +105,7 @@ func (e *xmlEncoder) encodeTopLevelMap(encoder *xml.Encoder, node *yaml.Node) er return err } } - return nil + return e.encodeComment(encoder, footComment(node)) } func (e *xmlEncoder) encodeStart(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error { @@ -141,6 +149,7 @@ func (e *xmlEncoder) doEncode(encoder *xml.Encoder, node *yaml.Node, start xml.S func (e *xmlEncoder) encodeComment(encoder *xml.Encoder, commentStr string) error { if commentStr != "" { + log.Debugf("encoding comment %v", commentStr) var comment xml.Comment = []byte(commentStr) err := encoder.EncodeToken(comment) if err != nil { @@ -151,6 +160,7 @@ func (e *xmlEncoder) encodeComment(encoder *xml.Encoder, commentStr string) erro } func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error { + e.encodeComment(encoder, headAndLineComment(node)) for i := 0; i < len(node.Content); i++ { value := node.Content[i] err := e.doEncode(encoder, value, start.Copy()) @@ -158,10 +168,12 @@ func (e *xmlEncoder) encodeArray(encoder *xml.Encoder, node *yaml.Node, start xm return err } } + e.encodeComment(encoder, footComment(node)) return nil } func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml.StartElement) error { + log.Debug("its a map") //first find all the attributes and put them on the start token for i := 0; i < len(node.Content); i += 2 { @@ -201,11 +213,19 @@ func (e *xmlEncoder) encodeMap(encoder *xml.Encoder, node *yaml.Node, start xml. } } else if key.Value == e.contentName { // directly encode the contents + err = e.encodeComment(encoder, headAndLineComment(value)) + if err != nil { + return err + } var charData xml.CharData = []byte(value.Value) err = encoder.EncodeToken(charData) if err != nil { return err } + err = e.encodeComment(encoder, footComment(value)) + if err != nil { + return err + } } err = e.encodeComment(encoder, footComment(key)) if err != nil { diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 82517414..534dbfd7 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -100,7 +100,6 @@ func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error { } if p.firstTimePrinting { - log.Debugf("its my first time *blush*") node := matchingNodes.Front().Value.(*CandidateNode) p.previousDocIndex = node.Document p.previousFileIndex = node.FileIndex diff --git a/pkg/yqlib/xml_test.go b/pkg/yqlib/xml_test.go index 72de0fa6..6ea23e56 100644 --- a/pkg/yqlib/xml_test.go +++ b/pkg/yqlib/xml_test.go @@ -65,6 +65,7 @@ var inputXmlWithComments = ` 3 + 4 @@ -89,14 +90,14 @@ cat: # after cat ` -var expectedRoundtripXmlWithComments = ` - + 3 - - 4 - - +for x -->3 + + 4 + + ` var yamlWithComments = `# above_cat @@ -116,75 +117,75 @@ var expectedXmlWithComments = `