diff --git a/go.mod b/go.mod
index d431a09..412538b 100644
--- a/go.mod
+++ b/go.mod
@@ -5,11 +5,9 @@ go 1.21
require (
github.com/rs/zerolog v1.28.0
github.com/stretchr/testify v1.8.4
- github.com/tamerh/xpath v1.0.0
)
require (
- github.com/antchfx/xpath v1.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
diff --git a/go.sum b/go.sum
index ca6bd53..d0e3e55 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,3 @@
-github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY=
-github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -16,8 +14,6 @@ github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/tamerh/xpath v1.0.0 h1:NccMES/Ej8slPCFDff73Kf6V1xu9hdbuKf2RyDsxf5Q=
-github.com/tamerh/xpath v1.0.0/go.mod h1:t0wnh72FQlOVEO20f2Dl3EoVxso9GnLREh1WTpvNmJQ=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
diff --git a/pkg/xixo/calback_test.go b/pkg/xixo/calback_test.go
index f40f94b..88bc58d 100644
--- a/pkg/xixo/calback_test.go
+++ b/pkg/xixo/calback_test.go
@@ -69,11 +69,10 @@ func TestJsonCallback(t *testing.T) {
editedRoot, err := xixo.XMLElementToJSONCallback(jsonCallback)(root)
assert.Nil(t, err)
- element1, err := editedRoot.SelectElement("element1")
-
- assert.Nil(t, err)
-
- assert.Equal(t, "newChildContent", element1.InnerText)
+ assert.Equal(t,
+ "\n\tnewChildContent\n\tContenu2 \n",
+ editedRoot.String(),
+ )
}
func badJSONCallback(source string) (string, error) {
diff --git a/pkg/xixo/callback.go b/pkg/xixo/callback.go
index fb08a8f..ce2fa74 100644
--- a/pkg/xixo/callback.go
+++ b/pkg/xixo/callback.go
@@ -11,11 +11,6 @@ type CallbackMap func(map[string]string) (map[string]string, error)
type CallbackJSON func(string) (string, error)
-type Attribute struct {
- Name string
- Value string
-}
-
// XMLElementToMapCallback transforms an XML element into a map, applies a callback function,
// adds parent attributes, and updates child elements.
func XMLElementToMapCallback(callback CallbackMap) Callback {
@@ -34,13 +29,11 @@ func XMLElementToMapCallback(callback CallbackMap) Callback {
// Extract parent attributes and add them to the XML element.
parentAttributes := extractParentAttributes(dict)
for _, attr := range parentAttributes {
- xmlElement.AddAttribute(attr.Name, attr.Value)
+ xmlElement.AddAttribute(attr)
}
- children, err := xmlElement.SelectElements("child::*")
- if err != nil {
- return nil, err
- }
+ children := xmlElement.childs
+
// Select child elements and update their text content and attributes.
childAttributes := extractChildAttributes(dict)
@@ -51,7 +44,7 @@ func XMLElementToMapCallback(callback CallbackMap) Callback {
if attributes, ok := childAttributes[child.Name]; ok {
for _, attr := range attributes {
- child.AddAttribute(attr.Name, attr.Value)
+ child.AddAttribute(attr)
}
}
}
@@ -64,13 +57,13 @@ func XMLElementToMapCallback(callback CallbackMap) Callback {
func extractExistedAttributes(xmlElement *XMLElement, dict map[string]string) {
for name, child := range xmlElement.Childs {
- for attr, value := range child[0].Attrs {
- dict[name+"@"+attr] = value
+ for attrName, attr := range child[0].Attrs {
+ dict[name+"@"+attrName] = attr.Value
}
}
- for attr, value := range xmlElement.Attrs {
- dict["@"+attr] = value
+ for attrName, attr := range xmlElement.Attrs {
+ dict["@"+attrName] = attr.Value
}
}
diff --git a/pkg/xixo/element.go b/pkg/xixo/element.go
index 973f639..0b4c27a 100644
--- a/pkg/xixo/element.go
+++ b/pkg/xixo/element.go
@@ -5,9 +5,38 @@ import (
"strings"
)
+type Quote string
+
+const (
+ SimpleQuote = "'"
+ DoubleQuotes = "\""
+)
+
+func ParseQuoteType(car byte) Quote {
+ if car == '\'' {
+ return SimpleQuote
+ }
+
+ return DoubleQuotes
+}
+
+type Attribute struct {
+ Name string
+ Value string
+ Quote Quote
+}
+
+func (attr Attribute) String() string {
+ if attr.Quote == SimpleQuote {
+ return fmt.Sprintf("%s='%s'", attr.Name, attr.Value)
+ }
+
+ return fmt.Sprintf("%s=\"%s\"", attr.Name, attr.Value)
+}
+
type XMLElement struct {
Name string
- Attrs map[string]string
+ Attrs map[string]Attribute
AttrKeys []string
InnerText string
Childs map[string][]XMLElement
@@ -16,7 +45,6 @@ type XMLElement struct {
// filled when xpath enabled
childs []*XMLElement
parent *XMLElement
- attrs []*xmlAttr
localName string
prefix string
@@ -24,21 +52,6 @@ type XMLElement struct {
autoClosable bool
}
-type xmlAttr struct {
- name string
- value string
-}
-
-// SelectElements finds child elements with the specified xpath expression.
-func (n *XMLElement) SelectElements(exp string) ([]*XMLElement, error) {
- return find(n, exp)
-}
-
-// SelectElement finds child elements with the specified xpath expression.
-func (n *XMLElement) SelectElement(exp string) (*XMLElement, error) {
- return findOne(n, exp)
-}
-
func (n *XMLElement) FirstChild() *XMLElement {
if n.childs == nil {
return nil
@@ -100,7 +113,7 @@ func (n *XMLElement) String() string {
attributes := n.Name + " "
for _, key := range n.AttrKeys {
- attributes += fmt.Sprintf("%s=\"%s\" ", key, n.Attrs[key])
+ attributes += n.Attrs[key].String() + " "
}
attributes = strings.Trim(attributes, " ")
@@ -119,30 +132,31 @@ func (n *XMLElement) String() string {
n.Name)
}
-func (n *XMLElement) AddAttribute(name string, value string) {
+func (n *XMLElement) AddAttribute(attr Attribute) {
if n.Attrs == nil {
- n.Attrs = make(map[string]string)
+ n.Attrs = make(map[string]Attribute)
}
// if name don't exsite in Attrs yet
- if _, ok := n.Attrs[name]; !ok {
+ if _, ok := n.Attrs[attr.Name]; !ok {
// Add un key in slice to keep the order of attributes
- n.AttrKeys = append(n.AttrKeys, name)
+ n.AttrKeys = append(n.AttrKeys, attr.Name)
+ } else {
+ attr.Quote = n.Attrs[attr.Name].Quote
}
// change the value of attribute
- n.Attrs[name] = value
+ n.Attrs[attr.Name] = attr
}
func NewXMLElement() *XMLElement {
return &XMLElement{
Name: "",
- Attrs: map[string]string{},
+ Attrs: map[string]Attribute{},
AttrKeys: make([]string, 0),
InnerText: "",
Childs: map[string][]XMLElement{},
Err: nil,
childs: []*XMLElement{},
parent: nil,
- attrs: []*xmlAttr{},
localName: "",
prefix: "",
}
diff --git a/pkg/xixo/element_test.go b/pkg/xixo/element_test.go
index b6d11f4..2ea3fb3 100644
--- a/pkg/xixo/element_test.go
+++ b/pkg/xixo/element_test.go
@@ -144,9 +144,11 @@ func TestAddAttributsShouldSaved(t *testing.T) {
root = xixo.NewXMLElement()
root.Name = name
- root.AddAttribute("foo", "bar")
+ attr := xixo.Attribute{"foo", "bar", xixo.SimpleQuote}
- expected := map[string]string{"foo": "bar"}
+ root.AddAttribute(attr)
+
+ expected := map[string]xixo.Attribute{"foo": attr}
assert.Equal(t, root.Attrs, expected)
}
@@ -157,7 +159,7 @@ func TestAddAttributsShouldInOutputWithString(t *testing.T) {
root := xixo.NewXMLElement()
root.Name = parentTag
root.InnerText = "Hello"
- root.AddAttribute("foo", "bar")
+ root.AddAttribute(xixo.Attribute{"foo", "bar", xixo.DoubleQuotes})
expected := "Hello"
assert.Equal(t, expected, root.String())
@@ -169,11 +171,11 @@ func TestEditAttributsShouldInOutputWithString(t *testing.T) {
root := xixo.NewXMLElement()
root.Name = parentTag
root.InnerText = "Hello"
- root.AddAttribute("foo", "bar")
+ root.AddAttribute(xixo.Attribute{"foo", "bar", xixo.DoubleQuotes})
expected := "Hello"
assert.Equal(t, expected, root.String())
- root.AddAttribute("foo", "bas")
+ root.AddAttribute(xixo.Attribute{"foo", "bas", xixo.DoubleQuotes})
expected = "Hello"
assert.Equal(t, expected, root.String())
diff --git a/pkg/xixo/parser.go b/pkg/xixo/parser.go
index 0343077..63bf065 100644
--- a/pkg/xixo/parser.go
+++ b/pkg/xixo/parser.go
@@ -495,7 +495,8 @@ search_close_tag:
if err != nil {
return nil, false, x.defaultError()
}
- result.AddAttribute(attr, attrVal)
+
+ result.AddAttribute(Attribute{attr, attrVal, ParseQuoteType(cur)})
x.scratch.reset()
diff --git a/pkg/xixo/parser_test.go b/pkg/xixo/parser_test.go
index 5475d96..a932527 100644
--- a/pkg/xixo/parser_test.go
+++ b/pkg/xixo/parser_test.go
@@ -233,6 +233,19 @@ func TestStreamWithoutModifications(t *testing.T) {
// {input: "icb1jcb2kcb3l", element: "a"},
// {input: "icb1jcb2k", element: "a"},
+ {input: "\n", element: "a"},
+ {input: "\n", element: "b"},
+
+ {input: "\n\n", element: "a"},
+ {input: "\n\n", element: "b"},
+ {input: "\n\n", element: "c"},
+
+ {input: "\n", element: "a"},
+ {input: "\n", element: "a"},
+
+ {input: "\n", element: "b"},
+ {input: "\n", element: "b"},
+
{input: "icb1jcb2k", element: "a"},
{input: "", element: "a"},
diff --git a/pkg/xixo/query.go b/pkg/xixo/query.go
deleted file mode 100644
index 154bebc..0000000
--- a/pkg/xixo/query.go
+++ /dev/null
@@ -1,209 +0,0 @@
-package xixo
-
-import (
- "fmt"
-
- "github.com/tamerh/xpath"
-)
-
-// CreateXPathNavigator creates a new xpath.NodeNavigator for the specified html.Node.
-func (x *XMLParser) CreateXPathNavigator(top *XMLElement) *XMLNodeNavigator {
- return &XMLNodeNavigator{curr: top, root: top, attr: -1}
-}
-
-// Compile the given xpath expression.
-func (x *XMLParser) CompileXpath(expr string) (*xpath.Expr, error) {
- exp, err := xpath.Compile(expr)
- if err != nil {
- return nil, err
- }
-
- return exp, nil
-}
-
-// CreateXPathNavigator creates a new xpath.NodeNavigator for the specified html.Node.
-func createXPathNavigator(top *XMLElement) *XMLNodeNavigator {
- return &XMLNodeNavigator{curr: top, root: top, attr: -1}
-}
-
-type XMLNodeNavigator struct {
- root, curr *XMLElement
- attr int
-}
-
-// Find searches the Node that matches by the specified XPath expr.
-func find(top *XMLElement, expr string) ([]*XMLElement, error) {
- exp, err := xpath.Compile(expr)
- if err != nil {
- return []*XMLElement{}, err
- }
-
- t := exp.Select(createXPathNavigator(top))
-
- var elems []*XMLElement
-
- for t.MoveNext() {
- current, ok := t.Current().(*XMLNodeNavigator)
- if !ok {
- return nil, fmt.Errorf("current is not a XMLNodeNavigator %v", current)
- }
-
- elems = append(elems, current.curr)
- }
-
- return elems, nil
-}
-
-// FindOne searches the Node that matches by the specified XPath expr,
-// and returns first element of matched.
-func findOne(top *XMLElement, expr string) (*XMLElement, error) {
- exp, err := xpath.Compile(expr)
- if err != nil {
- return nil, err
- }
-
- t := exp.Select(createXPathNavigator(top))
-
- var elem *XMLElement
-
- if t.MoveNext() {
- navigator, ok := t.Current().(*XMLNodeNavigator)
- if !ok {
- return nil, fmt.Errorf("Current is not a XMLNodeNavigator %v", navigator)
- }
-
- elem = navigator.curr
- }
-
- return elem, nil
-}
-
-func (x *XMLNodeNavigator) Current() *XMLElement {
- return x.curr
-}
-
-func (x *XMLNodeNavigator) NodeType() xpath.NodeType {
- if x.curr == x.root {
- return xpath.RootNode
- }
-
- if x.attr != -1 {
- return xpath.AttributeNode
- }
-
- return xpath.ElementNode
-}
-
-func (x *XMLNodeNavigator) LocalName() string {
- if x.attr != -1 {
- return x.curr.attrs[x.attr].name
- }
-
- return x.curr.localName
-}
-
-func (x *XMLNodeNavigator) Prefix() string {
- return x.curr.prefix
-}
-
-func (x *XMLNodeNavigator) Value() string {
- if x.attr != -1 {
- return x.curr.attrs[x.attr].value
- }
-
- return x.curr.InnerText
-}
-
-func (x *XMLNodeNavigator) Copy() xpath.NodeNavigator {
- n := *x
-
- return &n
-}
-
-func (x *XMLNodeNavigator) MoveToRoot() {
- x.curr = x.root
-}
-
-func (x *XMLNodeNavigator) MoveToParent() bool {
- if x.attr != -1 {
- x.attr = -1
-
- return true
- } else if node := x.curr.parent; node != nil {
- x.curr = node
-
- return true
- }
-
- return false
-}
-
-func (x *XMLNodeNavigator) MoveToNextAttribute() bool {
- if x.attr >= len(x.curr.attrs)-1 {
- return false
- }
- x.attr++
-
- return true
-}
-
-func (x *XMLNodeNavigator) MoveToChild() bool {
- if node := x.curr.FirstChild(); node != nil {
- x.curr = node
-
- return true
- }
-
- return false
-}
-
-func (x *XMLNodeNavigator) MoveToFirst() bool {
- if x.curr.parent != nil {
- node := x.curr.parent.FirstChild()
- if node != nil {
- x.curr = node
-
- return true
- }
- }
-
- return false
-}
-
-func (x *XMLNodeNavigator) MoveToPrevious() bool {
- node := x.curr.PrevSibling()
- if node != nil {
- x.curr = node
-
- return true
- }
-
- return false
-}
-
-func (x *XMLNodeNavigator) MoveToNext() bool {
- node := x.curr.NextSibling()
- if node != nil {
- x.curr = node
-
- return true
- }
-
- return false
-}
-
-func (x *XMLNodeNavigator) String() string {
- return x.Value()
-}
-
-func (x *XMLNodeNavigator) MoveTo(other xpath.NodeNavigator) bool {
- node, ok := other.(*XMLNodeNavigator)
- if !ok || node.root != x.root {
- return false
- }
-
- x.curr = node.curr
- x.attr = node.attr
-
- return true
-}