Skip to content

XML from MarshalIndent includes unwanted whitespaces #82

@bombsimon

Description

@bombsimon

Thank you for a great library! It's been really helpful for me to use while traversing XML.

I'm using the library to marshal XML from xmltree.Element. The XML generated will in some cases be read by a human and because of that I'm using xmltree.MarshalIndent. The problem is that the XML generated includes too much leading and trailing whitespaces so when being unmarshalled back to the original type (with standard library) the content includes unwanted newlines.

Example to reproduce

package main

import (
	"encoding/xml"
	"fmt"

	"aqwari.net/xml/xmltree"
)

type Test struct {
	Field1 string `xml:"parent>child1"`
	Field2 int    `xml:"parent>child2"`
	Field3 string `xml:"parent>child3"`
}

func main() {
	t := Test{"test string", 10, "another string"}

	// MarshalIndent with standard library
	std, _ := xml.MarshalIndent(t, "", "  ")

	// Parse the mashalled XML with xml tree and MarshalIndent the parsed tree
	elements, _ := xmltree.Parse(std)
	xt := xmltree.MarshalIndent(elements, "", "  ")

	// Print result from both libraries
	fmt.Println(string(std))
	fmt.Println("-")
	fmt.Println(string(xt))

	// Unmarshal the two generated byte slices with standard library
	t1 := Test{}
	t2 := Test{}

	xml.Unmarshal(std, &t1)
	xml.Unmarshal(xt, &t2)

	// The XML from stdandard library contains no whitespaces
	fmt.Printf(
		"Stdlib:  f1 '%s', f2 '%d', f3: '%s'\n",
		t1.Field1, t1.Field2, t1.Field3,
	)

	// The XML from xmltree library contains too much whitespaces
	fmt.Printf(
		"Xmltree: f1 '%s', f2 '%d', f3: '%s'\n",
		t2.Field1, t2.Field2, t2.Field3,
	)
}

Output

<Test>
  <parent>
    <child1>test string</child1>
    <child2>10</child2>
    <child3>another string</child3>
  </parent>
</Test>
-
<Test>
  <parent>
    <child1>
      test string
    </child1>
    <child2>
      10
    </child2>
    <child3>
      another string
    </child3>
  </parent>
</Test>

Stdlib:  f1 'test string', f2 '10', f3: 'another string'
Xmltree: f1 '
      test string
    ', f2 '10', f3: '
      another string
    '

This change to xmltree/marshal.go seems to fix my issues:

diff --git a/xmltree/marshal.go b/xmltree/marshal.go
index 001b63a..1c12495 100644
--- a/xmltree/marshal.go
+++ b/xmltree/marshal.go
@@ -115,16 +115,8 @@ func (e *encoder) encode(el, parent *Element, visited map[*Element]struct{}) err
 }
 
 func (e *encoder) WriteContent(content []byte, depth int) error {
-	if e.pretty {
-		io.WriteString(e.w, e.prefix)
-		for i := 0; i < depth; i++ {
-			io.WriteString(e.w, e.indent)
-		}
-	}
 	e.w.Write(content)
-	if e.pretty && !bytes.HasSuffix(content, []byte("\n")) {
-		io.WriteString(e.w, "\n")
-	}
+
 	return nil
 }
 
@@ -161,7 +153,9 @@ func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
 		return err
 	}
 	if e.pretty {
-		io.WriteString(e.w, "\n")
+		if len(el.Children) > 0 || len(el.Content) == 0 {
+			io.WriteString(e.w, "\n")
+		}
 	}
 	return nil
 }
@@ -169,7 +163,9 @@ func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
 func (e *encoder) encodeCloseTag(el *Element, depth int) error {
 	if e.pretty {
 		for i := 0; i < depth; i++ {
-			io.WriteString(e.w, e.indent)
+			if len(el.Children) > 0 {
+				io.WriteString(e.w, e.indent)
+			}
 		}
 	}
 	if err := tagTmpl.ExecuteTemplate(e.w, "end", el); err != nil {

So while running the example again with the above changes this is the output:

<Test>
  <parent>
    <child1>test string</child1>
    <child2>10</child2>
    <child3>another string</child3>
  </parent>
</Test>
-
<Test>
  <parent>
    <child1>test string</child1>
    <child2>10</child2>
    <child3>another string</child3>
  </parent>
</Test>

Stdlib:  f1 'test string', f2 '10', f3: 'another string'
Xmltree: f1 'test string', f2 '10', f3: 'another string'

Am I missing something or do you agree that this is a bug? If you agree, do you want me to create a PR?

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions