package dom import ( "encoding/xml" "fmt" "bytes" ) type Attr struct { Name xml.Name // Attribute namespace and name. Value string // Attribute value. } type Element struct { name xml.Name children []*Element parent *Element content string attributes []*Attr namespaces []*Namespace document *Document } func CreateElement(n string) *Element { element := &Element { name: xml.Name { Local: n } } element.children = make([]*Element, 0, 5) element.attributes = make([]*Attr, 0, 10) element.namespaces = make([]*Namespace, 0, 10) return element } func (node *Element) AddChild(child *Element) *Element { if child.parent != nil { child.parent.RemoveChild(child) } child.SetParent(node) node.children = append(node.children, child) return node } func (node *Element) RemoveChild(child *Element) *Element { p := -1 for i, v := range node.children { if v == child { p = i break } } if p == -1 { return node } copy(node.children[p:], node.children[p+1:]) node.children = node.children[0 : len(node.children)-1] child.parent = nil return node } func (node *Element) SetAttr(name string, value string) *Element { // namespaces? attr := &Attr{ Name: xml.Name { Local: name }, Value: value } node.attributes = append(node.attributes, attr) return node } func (node *Element) SetParent(parent *Element) *Element { node.parent = parent return node } func (node *Element) SetContent(content string) *Element { node.content = content return node } // Add a namespace declaration to this node func (node *Element) DeclareNamespace(ns Namespace) *Element { // check if we already have it prefix := node.namespacePrefix(ns.Uri) if prefix == ns.Prefix { return node } // add it node.namespaces = append(node.namespaces, &ns) return node } func (node *Element) DeclaredNamespaces() []*Namespace { return node.namespaces } func (node *Element) SetNamespace(prefix string, uri string) { resolved := node.namespacePrefix(uri) if resolved == "" { // we couldn't find the namespace, let's declare it at this node node.namespaces = append(node.namespaces, &Namespace { Prefix: prefix, Uri: uri }) } node.name.Space = uri } func (node *Element) Bytes(out *bytes.Buffer, indent bool, indentType string, level int) { empty := len(node.children) == 0 && node.content == "" content := node.content != "" // children := len(node.children) > 0 // ns := len(node.namespaces) > 0 // attrs := len(node.attributes) > 0 indentStr := "" nextLine := "" if indent { nextLine = "\n" for i := 0; i < level; i++ { indentStr += indentType } } if node.name.Local != "" { if len(node.name.Space) > 0 { // first find if ns has been declared, otherwise prefix := node.namespacePrefix(node.name.Space) fmt.Fprintf(out, "%s<%s:%s", indentStr, prefix, node.name.Local) } else { fmt.Fprintf(out, "%s<%s", indentStr, node.name.Local) } } // declared namespaces for _, v := range node.namespaces { prefix := node.namespacePrefix(v.Uri) fmt.Fprintf(out, ` xmlns:%s="%s"`, prefix, v.Uri) } // attributes for _, v := range node.attributes { if len(v.Name.Space) > 0 { prefix := node.namespacePrefix(v.Name.Space) fmt.Fprintf(out, ` %s:%s="%s"`, prefix, v.Name.Local, v.Value) } else { fmt.Fprintf(out, ` %s="%s"`, v.Name.Local, v.Value) } } // close tag if empty { fmt.Fprintf(out, "/>%s", nextLine) } else { if content { out.WriteRune('>') } else { fmt.Fprintf(out, ">%s", nextLine) } } if len(node.children) > 0 { for _, child := range node.children { child.Bytes(out, indent, indentType, level + 1) } } else if node.content != "" { //val := []byte(node.content) //xml.EscapeText(out, val) out.WriteString(node.content) } if !empty && len(node.name.Local) > 0 { var indentation string if content { indentation = "" } else { indentation = indentStr } if len(node.name.Space) > 0 { prefix := node.namespacePrefix(node.name.Space) fmt.Fprintf(out, "%s\n", indentation, prefix, node.name.Local) } else { fmt.Fprintf(out, "%s\n", indentation, node.name.Local) } } } // Finds the prefix of the given namespace if it has been declared // in this node or in one of its parent func (node *Element) namespacePrefix(uri string) string { for _, ns := range node.namespaces { if ns.Uri == uri { return ns.Prefix } } if node.parent == nil { return "" } return node.parent.namespacePrefix(uri) } func (node *Element) String() string { var b bytes.Buffer node.Bytes(&b, false, "", 0) return string(b.Bytes()) }