Skip to content

Commit

Permalink
Merge branch '#125'
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengchun committed Mar 1, 2025
2 parents 15733e6 + 486f36d commit 2c1ebad
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 16 deletions.
31 changes: 24 additions & 7 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ func (n *Node) OutputXML(self bool) string {
// OutputXMLWithOptions returns the text that including tags name.
func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string {
var b strings.Builder
n.WriteWithOptions(&b, opts...)
_ = n.WriteWithOptions(&b, opts...)
return b.String()
}

Expand Down Expand Up @@ -362,36 +362,53 @@ func (n *Node) WriteWithOptions(writer io.Writer, opts ...OutputOption) (err err
}

// AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'.
func AddAttr(n *Node, key, val string) {
// Returns false if the attribute already exists.
func AddAttr(n *Node, key, val string) bool {
if n.HasAttr(key) {
return false
}
attr := Attr{
Name: newXMLName(key),
Value: val,
}
n.Attr = append(n.Attr, attr)
return true
}

// HasAttr determines if an attribute exists.
func (n *Node) HasAttr(key string) bool {
name := newXMLName(key)
for _, attr := range n.Attr {
if attr.Name == name {
return true
}
}
return false
}

// SetAttr allows an attribute value with the specified name to be changed.
// If the attribute did not previously exist, it will be created.
func (n *Node) SetAttr(key, value string) {
func (n *Node) SetAttr(key, value string) bool {
name := newXMLName(key)
for i, attr := range n.Attr {
if attr.Name == name {
n.Attr[i].Value = value
return
return true
}
}
AddAttr(n, key, value)
return AddAttr(n, key, value)
}

// RemoveAttr removes the attribute with the specified name.
func (n *Node) RemoveAttr(key string) {
func (n *Node) RemoveAttr(key string) bool {
name := newXMLName(key)
for i, attr := range n.Attr {
if attr.Name == name {
n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
return
return true
}
}
return false
}

// AddChild adds a new node 'n' to a node 'parent' as its last child.
Expand Down
46 changes: 37 additions & 9 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,36 @@ func TestAddAttr(t *testing.T) {
key string
val string
expected string
ok bool
}{
{
name: "node has no existing attr",
n: &Node{Type: AttributeNode},
key: "ns:k1",
val: "v1",
expected: `< ns:k1="v1"></>`,
ok: true,
},
{
name: "node has existing attrs",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Local: "k1"}, Value: "v1"}}},
key: "k2",
val: "v2",
expected: `< k1="v1" k2="v2"></>`,
ok: true,
},
{
name: "node already has existing attr",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Local: "k1"}, Value: "v1"}}},
key: "k1",
val: "v1",
expected: `< k1="v1"></>`,
ok: false,
},
} {
t.Run(test.name, func(t *testing.T) {
AddAttr(test.n, test.key, test.val)
ok := AddAttr(test.n, test.key, test.val)
testTrue(t, ok == test.ok)
testValue(t, test.n.OutputXML(true), test.expected)
})
}
Expand All @@ -154,39 +166,45 @@ func TestSetAttr(t *testing.T) {
key string
val string
expected string
ok bool
}{
{
name: "node has no existing attr",
n: &Node{Type: AttributeNode},
key: "ns:k1",
val: "v1",
expected: `< ns:k1="v1"></>`,
ok: true,
},
{
name: "node has an existing attr, overwriting",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Space: "ns", Local: "k1"}, Value: "v1"}}},
key: "ns:k1",
val: "v2",
expected: `< ns:k1="v2"></>`,
ok: true,
},
{
name: "node has no existing attr, no ns",
n: &Node{Type: AttributeNode},
key: "k1",
val: "v1",
expected: `< k1="v1"></>`,
ok: true,
},
{
name: "node has an existing attr, no ns, overwriting",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Local: "k1"}, Value: "v1"}}},
key: "k1",
val: "v2",
expected: `< k1="v2"></>`,
ok: true,
},
} {

t.Run(test.name, func(t *testing.T) {
test.n.SetAttr(test.key, test.val)
ok := test.n.SetAttr(test.key, test.val)
testTrue(t, ok == test.ok)
testValue(t, test.n.OutputXML(true), test.expected)
})
}
Expand All @@ -198,48 +216,54 @@ func TestRemoveAttr(t *testing.T) {
n *Node
key string
expected string
ok bool
}{
{
name: "node has no existing attr",
n: &Node{Type: AttributeNode},
key: "ns:k1",
expected: `<></>`,
ok: false,
},
{
name: "node has an existing attr, overwriting",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Space: "ns", Local: "k1"}, Value: "v1"}}},
key: "ns:k1",
expected: `<></>`,
ok: true,
},
{
name: "node has no existing attr, no ns",
n: &Node{Type: AttributeNode},
key: "k1",
expected: `<></>`,
ok: false,
},
{
name: "node has an existing attr, no ns, overwriting",
n: &Node{Type: AttributeNode, Attr: []Attr{{Name: xml.Name{Local: "k1"}, Value: "v1"}}},
key: "k1",
expected: `<></>`,
ok: true,
},
} {

t.Run(test.name, func(t *testing.T) {
test.n.RemoveAttr(test.key)
ok := test.n.RemoveAttr(test.key)
testTrue(t, ok == test.ok)
testValue(t, test.n.OutputXML(true), test.expected)
})
}
}

func TestRemoveFromTree(t *testing.T) {
xml := `<?procinst?>
xmlStr := `<?procinst?>
<!--comment-->
<aaa><bbb/>
<ddd><eee><fff/></eee></ddd>
<ggg/></aaa>`
parseXML := func() *Node {
doc, err := Parse(strings.NewReader(xml))
doc, err := Parse(strings.NewReader(xmlStr))
testTrue(t, err == nil)
return doc
}
Expand Down Expand Up @@ -398,7 +422,8 @@ func TestEscapeValueWrite(t *testing.T) {
}

var b strings.Builder
root.Write(&b, true)
err = root.Write(&b, true)
testTrue(t, err == nil)
escapedInnerText := b.String()
if !strings.Contains(escapedInnerText, "&lt;*&gt;") {
t.Fatal("Inner Text has not been escaped")
Expand Down Expand Up @@ -448,7 +473,8 @@ func TestUnnecessaryEscapeValueWrite(t *testing.T) {
}

var b strings.Builder
root.Write(&b, true)
err = root.Write(&b, true)
testTrue(t, err == nil)
escapedInnerText := b.String()
if strings.Contains(escapedInnerText, "&#x9") {
t.Fatal("\\n has been escaped unnecessarily")
Expand Down Expand Up @@ -492,7 +518,8 @@ func TestHtmlUnescapeStringOriginStringWrite(t *testing.T) {
}

var b strings.Builder
root.Write(&b, false)
err = root.Write(&b, false)
testTrue(t, err == nil)
escapedInnerText := b.String()
unescapeString := html.UnescapeString(escapedInnerText)
if strings.Contains(unescapeString, "&amp;") {
Expand All @@ -516,7 +543,8 @@ func TestWriteWithNamespacePrefix(t *testing.T) {
s := `<?xml version="1.0" encoding="UTF-8"?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body></S:Body></S:Envelope>`
doc, _ := Parse(strings.NewReader(s))
var b strings.Builder
doc.Write(&b, false)
err := doc.Write(&b, false)
testTrue(t, err == nil)
if s != b.String() {
t.Fatal("xml document missing some characters")
}
Expand Down

0 comments on commit 2c1ebad

Please sign in to comment.