Commit 726e67ca authored by Paul Warren's avatar Paul Warren
Browse files

Merge branch 'master' of https://code.blinkace.com/go/xml

Conflicts:
	qname/qname.go
parents 968b2fdd 838bada2
Go XML tools
============
code.blinkace.com/go/xml/encoding/xml
=====================================
This is a fork of the standard encoding/xml with a number of changes, primarily
to deal with elements and attributes containing QName values.
(see https://github.com/golang/go/issues/12406)
In addition, it provides Encoder.XMLDeclaration to control the inclusion of an
XML declaration in the output.
code.blinkace.com/go/xml/qname
==============================
This package provides a QName type that implements the following interfaces
from the above package:
* Unmarshaler
* UnmarshalerAttr
* Marshaller
......@@ -130,7 +130,7 @@ type Encoder struct {
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
e := &Encoder{printer{Writer: bufio.NewWriter(w)}}
e := &Encoder{printer{Writer: bufio.NewWriter(w), prefixes: []map[string]string{make(map[string]string)}}}
e.p.encoder = e
return e
}
......@@ -143,6 +143,12 @@ func (enc *Encoder) Indent(prefix, indent string) {
enc.p.indent = indent
}
// Specify whether an XML declaration should be included in the output.
// Defaults to false.
func (enc *Encoder) XMLDeclaration(on bool) {
enc.p.xmlDeclaration = on
}
// Encode writes the XML encoding of v to the stream.
//
// See the documentation for Marshal for details about the conversion
......@@ -150,6 +156,9 @@ func (enc *Encoder) Indent(prefix, indent string) {
//
// Encode calls Flush before returning.
func (enc *Encoder) Encode(v interface{}) error {
if enc.p.xmlDeclaration {
enc.p.Write([]byte(Header))
}
err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil)
if err != nil {
return err
......@@ -294,22 +303,31 @@ func (enc *Encoder) Flush() error {
type printer struct {
*bufio.Writer
encoder *Encoder
seq int
indent string
prefix string
depth int
indentedIn bool
putNewline bool
attrNS map[string]string // map prefix -> name space
attrPrefix map[string]string // map name space -> prefix
prefixes []string
tags []Name
}
// createAttrPrefix finds the name space prefix attribute to use for the given name space,
// defining a new prefix if necessary. It returns the prefix.
func (p *printer) createAttrPrefix(url string) string {
encoder *Encoder
seq int
indent string
prefix string
depth int
indentedIn bool
putNewline bool
attrNS map[string]string // map prefix -> name space
attrPrefix map[string]string // map name space -> prefix
prefixes []map[string]string // stack of prefixes declared on each element
tags []Name
xmlDeclaration bool
}
// Returns a prefix for the specified namespace, defining a new one if
// necessary. Any new prefixes will be serialised on the next start element
// token to be encoded.
func (e *Encoder) GetPrefix(url string) string {
return e.p.getPrefix(url)
}
// getPrefix returns a prefix for the specified namespace, defining a new one if necessary.
// new prefixes are added to the map on the top of the p.prefixes stack, which
// will be output by writeStart
func (p *printer) getPrefix(url string) string {
if prefix := p.attrPrefix[url]; prefix != "" {
return prefix
}
......@@ -322,12 +340,6 @@ func (p *printer) createAttrPrefix(url string) string {
return "xml"
}
// Need to define a new name space.
if p.attrPrefix == nil {
p.attrPrefix = make(map[string]string)
p.attrNS = make(map[string]string)
}
// Pick a name. We try to use the final element of the path
// but fall back to _.
prefix := strings.TrimRight(url, "/")
......@@ -351,16 +363,10 @@ func (p *printer) createAttrPrefix(url string) string {
}
}
p.attrPrefix[url] = prefix
p.attrNS[prefix] = url
p.WriteString(`xmlns:`)
p.WriteString(prefix)
p.WriteString(`="`)
EscapeText(p, []byte(url))
p.WriteString(`" `)
/*
*/
p.prefixes = append(p.prefixes, prefix)
p.addPrefix(prefix, url)
return prefix
}
......@@ -371,17 +377,27 @@ func (p *printer) deleteAttrPrefix(prefix string) {
delete(p.attrNS, prefix)
}
func (p *printer) addPrefix(prefix, namespace string) {
if p.attrPrefix == nil {
p.attrPrefix = make(map[string]string)
p.attrNS = make(map[string]string)
}
p.prefixes[len(p.prefixes)-1][prefix] = namespace
p.attrNS[prefix] = namespace
p.attrPrefix[namespace] = prefix
}
func (p *printer) markPrefix() {
p.prefixes = append(p.prefixes, "")
p.prefixes = append(p.prefixes, make(map[string]string))
}
// Pop the list of NS declarations on an element, and delete them from the list
// of bindings in effect.
func (p *printer) popPrefix() {
for len(p.prefixes) > 0 {
prefix := p.prefixes[len(p.prefixes)-1]
p.prefixes = p.prefixes[:len(p.prefixes)-1]
if prefix == "" {
break
}
prefixes := p.prefixes[len(p.prefixes)-1]
p.prefixes = p.prefixes[:len(p.prefixes)-1]
for prefix, _ := range prefixes {
p.deleteAttrPrefix(prefix)
}
}
......@@ -657,7 +673,6 @@ func (p *printer) writeStart(start *StartElement) error {
}
p.tags = append(p.tags, start.Name)
p.markPrefix()
p.writeIndent(1)
p.WriteByte('<')
......@@ -677,7 +692,7 @@ func (p *printer) writeStart(start *StartElement) error {
}
p.WriteByte(' ')
if name.Space != "" {
p.WriteString(p.createAttrPrefix(name.Space))
p.WriteString(p.getPrefix(name.Space))
p.WriteByte(':')
}
p.WriteString(name.Local)
......@@ -685,6 +700,18 @@ func (p *printer) writeStart(start *StartElement) error {
p.EscapeString(attr.Value)
p.WriteByte('"')
}
/* Output any new namespace declarations that are required */
for prefix, ns := range p.prefixes[len(p.prefixes)-1] {
p.WriteString(` xmlns:`)
p.WriteString(prefix)
p.WriteString(`="`)
EscapeText(p, []byte(ns))
p.WriteString(`"`)
}
p.markPrefix()
p.WriteByte('>')
return nil
}
......
......@@ -84,6 +84,14 @@ func (qname *QName) UnmarshalXMLAttr(d *xml.Decoder, attr xml.Attr) error {
return nil
}
func (qname *QName) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
prefix := e.GetPrefix(qname.Namespace)
e.EncodeToken(start)
e.EncodeToken(xml.CharData([]byte(prefix + ":" + qname.Localname)))
e.EncodeToken(xml.EndElement{start.Name})
return nil
}
func (qname QName) String() string {
return "{" + qname.Namespace + "}" + qname.Localname
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment