This repository has been archived by the owner on Dec 12, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmarshal.go
166 lines (155 loc) · 4.46 KB
/
marshal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package xmltree
import (
"bytes"
"encoding/xml"
"io"
"text/template"
)
// NOTE(droyo) As of go1.5.1, the encoding/xml package does not resolve
// prefixes in attribute names. Therefore we add .Name.Space verbatim
// instead of trying to resolve it. One consequence is this is that we cannot
// rename prefixes without some work.
var tagTmpl = template.Must(template.New("Marshal XML tags").Parse(
`{{define "start" -}}
<{{.Scope.Prefix .Name -}}
{{range .StartElement.Attr}} {{$.Scope.Prefix .Name -}}="{{.Value}}"{{end -}}
{{range .NS }} xmlns{{ if .Local }}:{{ .Local }}{{end}}="{{ .Space }}"{{end -}}
{{if or .Children .Content}}>{{else}} />{{end}}
{{- end}}
{{define "end" -}}
</{{.Prefix .Name}}>{{end}}`))
// Marshal produces the XML encoding of an Element as a self-contained
// document. The xmltree package may adjust the declarations of XML
// namespaces if the Element has been modified, or is part of a larger scope,
// such that the document produced by Marshal is a valid XML document.
//
// The return value of Marshal will use the utf-8 encoding regardless of
// the original encoding of the source document.
func Marshal(el *Element) []byte {
var buf bytes.Buffer
if err := Encode(&buf, el); err != nil {
// bytes.Buffer.Write should never return an error
panic(err)
}
return buf.Bytes()
}
// MarshalIndent is like Marshal, but adds line breaks for each
// successive element. Each line begins with prefix and is
// followed by zero or more copies of indent according to the
// nesting depth.
func MarshalIndent(el *Element, prefix, indent string) []byte {
var buf bytes.Buffer
enc := encoder{
w: &buf,
prefix: prefix,
indent: indent,
pretty: true,
}
if err := enc.encode(el, nil, make(map[*Element]struct{})); err != nil {
panic(err)
}
return buf.Bytes()
}
// Encode writes the XML encoding of the Element to w.
// Encode returns any errors encountered writing to w.
func Encode(w io.Writer, el *Element) error {
enc := encoder{w: w}
return enc.encode(el, nil, make(map[*Element]struct{}))
}
type encoder struct {
w io.Writer
prefix, indent string
pretty bool
}
// This could be used to print a subset of an XML document, or a document
// that has been modified. In such an event, namespace declarations must
// be "pulled" in, so they can be resolved properly. This is trickier than
// just defining everything at the top level because there may be conflicts
// introduced by the modifications.
func (e *encoder) encode(el, parent *Element, visited map[*Element]struct{}) error {
if len(visited) > recursionLimit {
// We only return I/O errors
return nil
}
if _, ok := visited[el]; ok {
// We have a cycle. Leave a comment, but no error
e.w.Write([]byte("<!-- cycle detected -->"))
return nil
}
scope := diffScope(parent, el)
if err := e.encodeOpenTag(el, scope, len(visited)); err != nil {
return err
}
if len(el.Children) == 0 {
if len(el.Content) > 0 {
e.w.Write(el.Content)
} else {
return nil
}
}
for i := range el.Children {
visited[el] = struct{}{}
if err := e.encode(&el.Children[i], el, visited); err != nil {
return err
}
delete(visited, el)
}
if err := e.encodeCloseTag(el, len(visited)); err != nil {
return err
}
return nil
}
// diffScope returns the Scope of the child element, minus any
// identical namespace declaration in the parent's scope.
func diffScope(parent, child *Element) Scope {
if parent == nil { // root element
return child.Scope
}
childScope := child.Scope
parentScope := parent.Scope
for len(parentScope.ns) > 0 && len(childScope.ns) > 0 {
if childScope.ns[0] == parentScope.ns[0] {
childScope.ns = childScope.ns[1:]
parentScope.ns = parentScope.ns[1:]
} else {
break
}
}
return childScope
}
func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
if e.pretty {
for i := 0; i < depth; i++ {
io.WriteString(e.w, e.indent)
}
}
var tag = struct {
*Element
NS []xml.Name
}{Element: el, NS: scope.ns}
if err := tagTmpl.ExecuteTemplate(e.w, "start", tag); err != nil {
return err
}
if e.pretty {
if len(el.Children) > 0 || len(el.Content) == 0 {
io.WriteString(e.w, "\n")
}
}
return nil
}
func (e *encoder) encodeCloseTag(el *Element, depth int) error {
if e.pretty {
for i := 0; i < depth; i++ {
if len(el.Children) > 0 {
io.WriteString(e.w, e.indent)
}
}
}
if err := tagTmpl.ExecuteTemplate(e.w, "end", el); err != nil {
return err
}
if e.pretty {
io.WriteString(e.w, "\n")
}
return nil
}