Adds optional indented output for the Node.String() and Node.Bytes()

methods. Set the global `IndentPrefix` var to a tab or spaces to
enable indented output. The existing API has not been changed.

This addresses issue #7.
This commit is contained in:
jimt 2012-11-25 23:46:56 +01:00
parent 88d4341915
commit c085b35fb9
4 changed files with 92 additions and 17 deletions

View File

@ -29,7 +29,6 @@ package xmlx
import ( import (
"bytes" "bytes"
//"code.google.com/p/go-charset/charset"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
@ -206,6 +205,10 @@ func (this *Document) SaveBytes() []byte {
if this.SaveDocType { if this.SaveDocType {
b.WriteString(fmt.Sprintf(`<?xml version="%s" encoding="%s" standalone="%s"?>`, b.WriteString(fmt.Sprintf(`<?xml version="%s" encoding="%s" standalone="%s"?>`,
this.Version, this.Encoding, this.StandAlone)) this.Version, this.Encoding, this.StandAlone))
if len(IndentPrefix) > 0 {
b.WriteByte('\n')
}
} }
b.Write(this.Root.Bytes()) b.Write(this.Root.Bytes())

52
node.go
View File

@ -9,6 +9,7 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"strconv" "strconv"
"strings"
) )
const ( const (
@ -19,6 +20,11 @@ const (
NT_ELEMENT NT_ELEMENT
) )
// IndentPrefix holds the value for a single identation level, if one
// chooses to want indentation in the node.String() and node.Bytes() output.
// This would normally be set to a single tab, or a number of spaces.
var IndentPrefix = ""
type Attr struct { type Attr struct {
Name xml.Name // Attribute namespace and name. Name xml.Name // Attribute namespace and name.
Value string // Attribute value. Value string // Attribute value.
@ -45,7 +51,7 @@ func NewNode(tid byte) *Node {
// This wraps the standard xml.Unmarshal function and supplies this particular // This wraps the standard xml.Unmarshal function and supplies this particular
// node as the content to be unmarshalled. // node as the content to be unmarshalled.
func (this *Node) Unmarshal(obj interface{}) error { func (this *Node) Unmarshal(obj interface{}) error {
return xml.NewDecoder(bytes.NewBuffer(this.Bytes())).Decode(obj) return xml.NewDecoder(bytes.NewBuffer(this.bytes(0))).Decode(obj)
} }
// Get node value as string // Get node value as string
@ -258,18 +264,20 @@ func rec_SelectNodes(cn *Node, namespace, name string, list *[]*Node, recurse bo
// Note that NT_ROOT is a special-case empty node used as the root for a // Note that NT_ROOT is a special-case empty node used as the root for a
// Document. This one has no representation by itself. It merely forwards the // Document. This one has no representation by itself. It merely forwards the
// String() call to it's child nodes. // String() call to it's child nodes.
func (this *Node) Bytes() (b []byte) { func (this *Node) Bytes() []byte { return this.bytes(0) }
func (this *Node) bytes(indent int) (b []byte) {
switch this.Type { switch this.Type {
case NT_PROCINST: case NT_PROCINST:
b = this.printProcInst() b = this.printProcInst(indent)
case NT_COMMENT: case NT_COMMENT:
b = this.printComment() b = this.printComment(indent)
case NT_DIRECTIVE: case NT_DIRECTIVE:
b = this.printDirective() b = this.printDirective(indent)
case NT_ELEMENT: case NT_ELEMENT:
b = this.printElement() b = this.printElement(indent)
case NT_ROOT: case NT_ROOT:
b = this.printRoot() b = this.printRoot(indent)
} }
return return
} }
@ -279,32 +287,38 @@ func (this *Node) Bytes() (b []byte) {
// Document. This one has no representation by itself. It merely forwards the // Document. This one has no representation by itself. It merely forwards the
// String() call to it's child nodes. // String() call to it's child nodes.
func (this *Node) String() (s string) { func (this *Node) String() (s string) {
return string(this.Bytes()) return string(this.bytes(0))
} }
func (this *Node) printRoot() []byte { func (this *Node) printRoot(indent int) []byte {
var b bytes.Buffer var b bytes.Buffer
for _, v := range this.Children { for _, v := range this.Children {
b.Write(v.Bytes()) b.Write(v.bytes(indent))
} }
return b.Bytes() return b.Bytes()
} }
func (this *Node) printProcInst() []byte { func (this *Node) printProcInst(indent int) []byte {
return []byte("<?" + this.Target + " " + this.Value + "?>") return []byte("<?" + this.Target + " " + this.Value + "?>")
} }
func (this *Node) printComment() []byte { func (this *Node) printComment(indent int) []byte {
return []byte("<!-- " + this.Value + " -->") return []byte("<!-- " + this.Value + " -->")
} }
func (this *Node) printDirective() []byte { func (this *Node) printDirective(indent int) []byte {
return []byte("<!" + this.Value + "!>") return []byte("<!" + this.Value + "!>")
} }
func (this *Node) printElement() []byte { func (this *Node) printElement(indent int) []byte {
var b bytes.Buffer var b bytes.Buffer
lineSuffix, linePrefix := "", strings.Repeat(IndentPrefix, indent)
if len(IndentPrefix) > 0 {
lineSuffix = "\n"
}
b.WriteString(linePrefix)
if len(this.Name.Space) > 0 { if len(this.Name.Space) > 0 {
b.WriteRune('<') b.WriteRune('<')
b.WriteString(this.Name.Space) b.WriteString(this.Name.Space)
@ -325,16 +339,23 @@ func (this *Node) printElement() []byte {
if len(this.Children) == 0 && len(this.Value) == 0 { if len(this.Children) == 0 && len(this.Value) == 0 {
b.WriteString(" />") b.WriteString(" />")
b.WriteString(lineSuffix)
return b.Bytes() return b.Bytes()
} }
b.WriteRune('>') b.WriteRune('>')
if len(this.Value) == 0 {
b.WriteString(lineSuffix)
}
for _, v := range this.Children { for _, v := range this.Children {
b.Write(v.Bytes()) b.Write(v.bytes(indent + 1))
} }
b.WriteString(this.Value) b.WriteString(this.Value)
if len(this.Value) == 0 {
b.WriteString(linePrefix)
}
if len(this.Name.Space) > 0 { if len(this.Name.Space) > 0 {
b.WriteString("</") b.WriteString("</")
b.WriteString(this.Name.Space) b.WriteString(this.Name.Space)
@ -346,6 +367,7 @@ func (this *Node) printElement() []byte {
b.WriteString(this.Name.Local) b.WriteString(this.Name.Local)
b.WriteRune('>') b.WriteRune('>')
} }
b.WriteString(lineSuffix)
return b.Bytes() return b.Bytes()
} }

View File

@ -1 +1,50 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="0.91"><channel><title>WriteTheWeb</title><link>http://writetheweb.com</link><description>News for web users that write back</description><language>en-us</language><copyright>Copyright 2000, WriteTheWeb team.</copyright><managingEditor>editor@writetheweb.com</managingEditor><webMaster>webmaster@writetheweb.com</webMaster><image><title>WriteTheWeb</title><url>http://writetheweb.com/images/mynetscape88.gif</url><link>http://writetheweb.com</link><width>88</width><height>31</height><description>News for web users that write back</description></image><item><title>Giving the world a pluggable Gnutella</title><link>http://writetheweb.com/read.php?item=24</link><description>WorldOS is a framework on which to build programs that work like Freenet or Gnutella -allowing distributed applications using peer-to-peer routing.</description></item><item><title>Syndication discussions hot up</title><link>http://writetheweb.com/read.php?item=23</link><description>After a period of dormancy, the Syndication mailing list has become active again, with contributions from leaders in traditional media and Web syndication.</description></item><item><title>Personal web server integrates file sharing and messaging</title><link>http://writetheweb.com/read.php?item=22</link><description>The Magi Project is an innovative project to create a combined personal web server and messaging system that enables the sharing and synchronization of information across desktop, laptop and palmtop devices.</description></item><item><title>Syndication and Metadata</title><link>http://writetheweb.com/read.php?item=21</link><description>RSS is probably the best known metadata format around. RDF is probably one of the least understood. In this essay, published on my O'Reilly Network weblog, I argue that the next generation of RSS should be based on RDF.</description></item><item><title>UK bloggers get organised</title><link>http://writetheweb.com/read.php?item=20</link><description>Looks like the weblogs scene is gathering pace beyond the shores of the US. There's now a UK-specific page on weblogs.com, and a mailing list at egroups.</description></item><item><title>Yournamehere.com more important than anything</title><link>http://writetheweb.com/read.php?item=19</link><description>Whatever you're publishing on the web, your site name is the most valuable asset you have, according to Carl Steadman.</description></item></channel></rss> <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="0.91">
<channel>
<title>WriteTheWeb</title>
<link>http://writetheweb.com</link>
<description>News for web users that write back</description>
<language>en-us</language>
<copyright>Copyright 2000, WriteTheWeb team.</copyright>
<managingEditor>editor@writetheweb.com</managingEditor>
<webMaster>webmaster@writetheweb.com</webMaster>
<image>
<title>WriteTheWeb</title>
<url>http://writetheweb.com/images/mynetscape88.gif</url>
<link>http://writetheweb.com</link>
<width>88</width>
<height>31</height>
<description>News for web users that write back</description>
</image>
<item>
<title>Giving the world a pluggable Gnutella</title>
<link>http://writetheweb.com/read.php?item=24</link>
<description>WorldOS is a framework on which to build programs that work like Freenet or Gnutella -allowing distributed applications using peer-to-peer routing.</description>
</item>
<item>
<title>Syndication discussions hot up</title>
<link>http://writetheweb.com/read.php?item=23</link>
<description>After a period of dormancy, the Syndication mailing list has become active again, with contributions from leaders in traditional media and Web syndication.</description>
</item>
<item>
<title>Personal web server integrates file sharing and messaging</title>
<link>http://writetheweb.com/read.php?item=22</link>
<description>The Magi Project is an innovative project to create a combined personal web server and messaging system that enables the sharing and synchronization of information across desktop, laptop and palmtop devices.</description>
</item>
<item>
<title>Syndication and Metadata</title>
<link>http://writetheweb.com/read.php?item=21</link>
<description>RSS is probably the best known metadata format around. RDF is probably one of the least understood. In this essay, published on my O'Reilly Network weblog, I argue that the next generation of RSS should be based on RDF.</description>
</item>
<item>
<title>UK bloggers get organised</title>
<link>http://writetheweb.com/read.php?item=20</link>
<description>Looks like the weblogs scene is gathering pace beyond the shores of the US. There's now a UK-specific page on weblogs.com, and a mailing list at egroups.</description>
</item>
<item>
<title>Yournamehere.com more important than anything</title>
<link>http://writetheweb.com/read.php?item=19</link>
<description>Whatever you're publishing on the web, your site name is the most valuable asset you have, according to Carl Steadman.</description>
</item>
</channel>
</rss>

View File

@ -74,6 +74,7 @@ func TestSave(t *testing.T) {
return return
} }
IndentPrefix = "\t"
if err := doc.SaveFile("test1.xml"); err != nil { if err := doc.SaveFile("test1.xml"); err != nil {
t.Errorf("SaveFile(): %s", err) t.Errorf("SaveFile(): %s", err)
return return