]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/xml: add MarshalIndent and move the example
authorGustavo Niemeyer <gustavo@niemeyer.net>
Thu, 16 Feb 2012 04:01:46 +0000 (02:01 -0200)
committerGustavo Niemeyer <gustavo@niemeyer.net>
Thu, 16 Feb 2012 04:01:46 +0000 (02:01 -0200)
An unindented XML example is hard to follow. MarshalIndent
allows moving the example over to a test file (and fixing it).

R=golang-dev, r, gustavo, r, rsc
CC=golang-dev
https://golang.org/cl/5674050

src/pkg/encoding/xml/example_test.go [new file with mode: 0644]
src/pkg/encoding/xml/marshal.go

diff --git a/src/pkg/encoding/xml/example_test.go b/src/pkg/encoding/xml/example_test.go
new file mode 100644 (file)
index 0000000..2f0c174
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xml_test
+
+import (
+       "encoding/xml"
+       "fmt"
+       "os"
+)
+
+//     <person id="13">
+//             <name>
+//                     <first>John</first>
+//                     <last>Doe</last>
+//             </name>
+//             <age>42</age>
+//             <Married>false</Married>
+//             <!-- Need more fields. -->
+//     </person>
+func ExampleMarshalIndent() {
+       type Person struct {
+               XMLName   xml.Name `xml:"person"`
+               Id        int      `xml:"id,attr"`
+               FirstName string   `xml:"name>first"`
+               LastName  string   `xml:"name>last"`
+               Age       int      `xml:"age"`
+               Height    float32  `xml:"height,omitempty"`
+               Married   bool
+               Comment   string `xml:",comment"`
+       }
+
+       v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
+       v.Comment = " Need more fields. "
+
+       output, err := xml.MarshalIndent(v, "\t", "\t")
+       if err != nil {
+               fmt.Printf("error: %v\n", err)
+       }
+
+       os.Stdout.Write(output)
+}
index a96c523d55316e2f0530b077f49fb2dfbaf49a69..25d88c4619b2d0d9cbb65b256d90f671ea561e5a 100644 (file)
@@ -60,32 +60,9 @@ const (
 //
 // If a field uses a tag "a>b>c", then the element c will be nested inside
 // parent elements a and b.  Fields that appear next to each other that name
-// the same parent will be enclosed in one XML element.  For example:
+// the same parent will be enclosed in one XML element.
 //
-//     type Result struct {
-//             XMLName   xml.Name `xml:"result"`
-//             Id        int      `xml:"id,attr"`
-//             FirstName string   `xml:"person>name>first"`
-//             LastName  string   `xml:"person>name>last"`
-//             Age       int      `xml:"person>age"`
-//             Height    float    `xml:"person>height,omitempty"`
-//             Married   bool     `xml:"person>married"`
-//     }
-//
-//     xml.Marshal(&Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42})
-//
-// would be marshalled as:
-//
-//     <result>
-//             <person id="13">
-//                     <name>
-//                             <first>John</first>
-//                             <last>Doe</last>
-//                     </name>
-//                     <age>42</age>
-//                     <married>false</married>
-//             </person>
-//     </result>
+// See MarshalIndent for an example.
 //
 // Marshal will return an error if asked to marshal a channel, function, or map.
 func Marshal(v interface{}) ([]byte, error) {
@@ -96,6 +73,22 @@ func Marshal(v interface{}) ([]byte, error) {
        return b.Bytes(), nil
 }
 
+// MarshalIndent works like Marshal, but each XML element begins on a new
+// indented line that starts with prefix and is followed by one or more
+// copies of indent according to the nesting depth.
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
+       var b bytes.Buffer
+       enc := NewEncoder(&b)
+       enc.prefix = prefix
+       enc.indent = indent
+       err := enc.marshalValue(reflect.ValueOf(v), nil)
+       enc.Flush()
+       if err != nil {
+               return nil, err
+       }
+       return b.Bytes(), nil
+}
+
 // An Encoder writes XML data to an output stream.
 type Encoder struct {
        printer
@@ -103,7 +96,7 @@ type Encoder struct {
 
 // NewEncoder returns a new encoder that writes to w.
 func NewEncoder(w io.Writer) *Encoder {
-       return &Encoder{printer{bufio.NewWriter(w)}}
+       return &Encoder{printer{Writer: bufio.NewWriter(w)}}
 }
 
 // Encode writes the XML encoding of v to the stream.
@@ -118,8 +111,14 @@ func (enc *Encoder) Encode(v interface{}) error {
 
 type printer struct {
        *bufio.Writer
+       indent     string
+       prefix     string
+       depth      int
+       indentedIn bool
 }
 
+// marshalValue writes one or more XML elements representing val.
+// If val was obtained from a struct field, finfo must have its details.
 func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
        if !val.IsValid() {
                return nil
@@ -177,6 +176,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
                }
        }
 
+       p.writeIndent(1)
        p.WriteByte('<')
        p.WriteString(name)
 
@@ -216,6 +216,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
                return err
        }
 
+       p.writeIndent(-1)
        p.WriteByte('<')
        p.WriteByte('/')
        p.WriteString(name)
@@ -294,6 +295,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
                        if vf.Len() == 0 {
                                continue
                        }
+                       p.writeIndent(0)
                        p.WriteString("<!--")
                        dashDash := false
                        dashLast := false
@@ -352,6 +354,33 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
        return nil
 }
 
+func (p *printer) writeIndent(depthDelta int) {
+       if len(p.prefix) == 0 && len(p.indent) == 0 {
+               return
+       }
+       if depthDelta < 0 {
+               p.depth--
+               if p.indentedIn {
+                       p.indentedIn = false
+                       return
+               }
+               p.indentedIn = false
+       }
+       p.WriteByte('\n')
+       if len(p.prefix) > 0 {
+               p.WriteString(p.prefix)
+       }
+       if len(p.indent) > 0 {
+               for i := 0; i < p.depth; i++ {
+                       p.WriteString(p.indent)
+               }
+       }
+       if depthDelta > 0 {
+               p.depth++
+               p.indentedIn = true
+       }
+}
+
 type parentStack struct {
        *printer
        stack []string
@@ -367,20 +396,20 @@ func (s *parentStack) trim(parents []string) {
                        break
                }
        }
-
        for i := len(s.stack) - 1; i >= split; i-- {
+               s.writeIndent(-1)
                s.WriteString("</")
                s.WriteString(s.stack[i])
                s.WriteByte('>')
        }
-
        s.stack = parents[:split]
 }
 
 // push adds parent elements to the stack and writes open tags.
 func (s *parentStack) push(parents []string) {
        for i := 0; i < len(parents); i++ {
-               s.WriteString("<")
+               s.writeIndent(1)
+               s.WriteByte('<')
                s.WriteString(parents[i])
                s.WriteByte('>')
        }