diff --git a/pkg/xixo/calback_test.go b/pkg/xixo/calback_test.go
index de8e6e2..f40f94b 100644
--- a/pkg/xixo/calback_test.go
+++ b/pkg/xixo/calback_test.go
@@ -8,7 +8,13 @@ import (
"github.com/stretchr/testify/assert"
)
-const newChildContent = "newChildContent"
+const (
+ newChildContent = "newChildContent"
+ rootXML = `
+ Hello world !
+ Contenu2
+`
+)
func mapCallback(dict map[string]string) (map[string]string, error) {
dict["element1"] = newChildContent
@@ -20,19 +26,23 @@ func mapCallback(dict map[string]string) (map[string]string, error) {
func TestMapCallback(t *testing.T) {
t.Parallel()
- element1 := createTree()
- //nolint
- assert.Equal(t, "\n Hello world !\n Contenu2 \n", element1.String())
+ element1 := createTreeFromXMLString(rootXML)
+
+ assert.Equal(t, rootXML, element1.String())
editedElement1, err := xixo.XMLElementToMapCallback(mapCallback)(element1)
assert.Nil(t, err)
text := editedElement1.FirstChild().InnerText
- assert.Equal(t, "newChildContent", text)
+ assert.Equal(t, newChildContent, text)
- //nolint
- assert.Equal(t, "\n newChildContent\n Contenu2 \n", editedElement1.String())
+ expected := `
+ newChildContent
+ Contenu2
+`
+
+ assert.Equal(t, expected, editedElement1.String())
}
func jsonCallback(source string) (string, error) {
@@ -54,7 +64,7 @@ func jsonCallback(source string) (string, error) {
func TestJsonCallback(t *testing.T) {
t.Parallel()
- root := createTree()
+ root := createTreeFromXMLString(rootXML)
editedRoot, err := xixo.XMLElementToJSONCallback(jsonCallback)(root)
assert.Nil(t, err)
@@ -73,7 +83,7 @@ func badJSONCallback(source string) (string, error) {
func TestBadJsonCallback(t *testing.T) {
t.Parallel()
- element1 := createTree()
+ element1 := createTreeFromXMLString(rootXML)
_, err := xixo.XMLElementToJSONCallback(badJSONCallback)(element1)
@@ -83,9 +93,14 @@ func TestBadJsonCallback(t *testing.T) {
func TestMapCallbackWithAttributs(t *testing.T) {
t.Parallel()
- element1 := createTreeWithAttribut()
- //nolint
- assert.Equal(t, "\n Hello world !\n Contenu2 \n", element1.String())
+ rootXML := `
+ Hello world !
+ Contenu2
+ `
+
+ element1 := createTreeFromXMLString(rootXML)
+
+ assert.Equal(t, rootXML, element1.String())
editedElement1, err := xixo.XMLElementToMapCallback(mapCallbackAttributs)(element1)
assert.Nil(t, err)
@@ -94,8 +109,12 @@ func TestMapCallbackWithAttributs(t *testing.T) {
assert.Equal(t, "newChildContent", text)
- //nolint
- assert.Equal(t, "\n newChildContent\n Contenu2 \n", editedElement1.String())
+ expected := `
+ newChildContent
+ Contenu2
+ `
+
+ assert.Equal(t, expected, editedElement1.String())
}
func mapCallbackAttributs(dict map[string]string) (map[string]string, error) {
@@ -108,9 +127,14 @@ func mapCallbackAttributs(dict map[string]string) (map[string]string, error) {
func TestMapCallbackWithAttributsParentAndChilds(t *testing.T) {
t.Parallel()
- element1 := createTreeWithAttributParent()
- //nolint
- assert.Equal(t, "\n Hello world !\n Contenu2 \n", element1.String())
+ rootXML := `
+ Hello world !
+ Contenu2
+ `
+
+ element1 := createTreeFromXMLString(rootXML)
+
+ assert.Equal(t, rootXML, element1.String())
editedElement1, err := xixo.XMLElementToMapCallback(mapCallbackAttributsWithParent)(element1)
assert.Nil(t, err)
@@ -119,8 +143,12 @@ func TestMapCallbackWithAttributsParentAndChilds(t *testing.T) {
assert.Equal(t, "newChildContent", text)
- //nolint
- assert.Equal(t, "\n newChildContent\n Contenu2 \n", editedElement1.String())
+ expected := `
+ newChildContent
+ Contenu2
+ `
+
+ assert.Equal(t, expected, editedElement1.String())
}
func mapCallbackAttributsWithParent(dict map[string]string) (map[string]string, error) {
diff --git a/pkg/xixo/driver_test.go b/pkg/xixo/driver_test.go
index 938e087..8799aea 100644
--- a/pkg/xixo/driver_test.go
+++ b/pkg/xixo/driver_test.go
@@ -33,7 +33,7 @@ func TestFuncDriverEdit(t *testing.T) {
assert.True(t, called)
- expected := "\n innerTextb\n"
+ expected := "innerTextb"
assert.Equal(t, expected, writer.String())
}
@@ -59,7 +59,7 @@ func TestFuncDriverEditEmptyElement(t *testing.T) {
assert.True(t, called)
- expected := "\n \n"
+ expected := ""
assert.Equal(t, expected, writer.String())
}
@@ -69,9 +69,9 @@ func TestFuncDriverEdit2subscribers(t *testing.T) {
// Create a reader with an XML string, an empty writer, a callback function, and a driver.
reader := bytes.NewBufferString(
`
- innerTexta1
- innerTexta2
-`,
+ innerTexta1
+ innerTexta2
+ `,
)
writer := bytes.Buffer{}
called1, called2 := false, false
@@ -79,12 +79,14 @@ func TestFuncDriverEdit2subscribers(t *testing.T) {
subscribers := map[string]xixo.CallbackMap{
"root1": func(input map[string]string) (map[string]string, error) {
called1 = true
+ assert.Equal(t, input["element1"], "innerTexta1")
input["element1"] = "innerTextb1"
return input, nil
},
"root2": func(input map[string]string) (map[string]string, error) {
called2 = true
+ assert.Equal(t, "innerTexta2", input["element2"])
input["element2"] = "innerTextb2"
return input, nil
@@ -101,12 +103,8 @@ func TestFuncDriverEdit2subscribers(t *testing.T) {
assert.True(t, called2)
expected := `
-
- innerTextb1
-
-
- innerTextb2
-
-`
+ innerTextb1
+ innerTextb2
+ `
assert.Equal(t, expected, writer.String())
}
diff --git a/pkg/xixo/element.go b/pkg/xixo/element.go
index 9ab4b23..e843a8f 100644
--- a/pkg/xixo/element.go
+++ b/pkg/xixo/element.go
@@ -5,6 +5,15 @@ import (
"strings"
)
+type CommentElement struct {
+ OuterTextBefore string
+ Comment string
+}
+
+func (c CommentElement) String() string {
+ return fmt.Sprintf("%s", c.OuterTextBefore, c.Comment)
+}
+
type XMLElement struct {
Name string
Attrs map[string]string
@@ -12,12 +21,16 @@ type XMLElement struct {
InnerText string
Childs map[string][]XMLElement
Err error
+
// filled when xpath enabled
childs []*XMLElement
parent *XMLElement
attrs []*xmlAttr
localName string
prefix string
+
+ outerTextBefore string
+ comments []CommentElement
}
type xmlAttr struct {
@@ -91,11 +104,7 @@ func (n *XMLElement) String() string {
xmlChilds := ""
for node := n.FirstChild(); node != nil; node = node.NextSibling() {
- xmlChilds += " " + node.String() + "\n"
- }
-
- if len(xmlChilds) > 0 {
- xmlChilds = "\n" + xmlChilds
+ xmlChilds += node.String()
}
attributes := n.Name + " "
@@ -105,10 +114,17 @@ func (n *XMLElement) String() string {
attributes = strings.Trim(attributes, " ")
- return fmt.Sprintf("<%s>%s%s%s>",
+ commentsString := ""
+ for _, comment := range n.comments {
+ commentsString += comment.String()
+ }
+
+ return fmt.Sprintf("%s<%s>%s%s%s%s>",
+ n.outerTextBefore,
attributes,
- n.InnerText,
+ commentsString,
xmlChilds,
+ n.InnerText,
n.Name)
}
@@ -125,6 +141,10 @@ func (n *XMLElement) AddAttribute(name string, value string) {
n.Attrs[name] = value
}
+func (n *XMLElement) AddComment(comment CommentElement) {
+ n.comments = append(n.comments, comment)
+}
+
func NewXMLElement() *XMLElement {
return &XMLElement{
Name: "",
diff --git a/pkg/xixo/element_test.go b/pkg/xixo/element_test.go
index dbc253d..b6d11f4 100644
--- a/pkg/xixo/element_test.go
+++ b/pkg/xixo/element_test.go
@@ -9,15 +9,11 @@ import (
"github.com/stretchr/testify/assert"
)
-const parentTag = "root"
-
-func createTree() *xixo.XMLElement {
- rootXML := `
-
- Hello world !
- Contenu2
- `
+const (
+ parentTag = "root"
+)
+func createTreeFromXMLString(rootXML string) *xixo.XMLElement {
var root *xixo.XMLElement
parser := xixo.NewXMLParser(bytes.NewBufferString(rootXML), io.Discard).EnableXpath()
@@ -35,10 +31,11 @@ func createTree() *xixo.XMLElement {
return root
}
-func createTreeWithAttribut() *xixo.XMLElement {
- rootXML := `
-
- Hello world !
+func TestElementStringShouldReturnXML(t *testing.T) {
+ t.Parallel()
+
+ rootXML := `
+ Hello world !
Contenu2
`
@@ -52,51 +49,33 @@ func createTreeWithAttribut() *xixo.XMLElement {
})
err := parser.Stream()
- if err != nil {
- return nil
- }
-
- return root
-}
+ assert.Nil(t, err)
-func createTreeWithAttributParent() *xixo.XMLElement {
- rootXML := `
-
- Hello world !
+ expected := `
+ Hello world !
Contenu2
`
- var root *xixo.XMLElement
-
- parser := xixo.NewXMLParser(bytes.NewBufferString(rootXML), io.Discard).EnableXpath()
- parser.RegisterCallback("root", func(x *xixo.XMLElement) (*xixo.XMLElement, error) {
- root = x
-
- return x, nil
- })
-
- err := parser.Stream()
- if err != nil {
- return nil
- }
-
- return root
+ assert.Equal(t, expected, root.String())
}
-func TestElementStringShouldReturnXML(t *testing.T) {
+func TestElementStringShouldReturnXMLWithSameOrder(t *testing.T) {
t.Parallel()
- rootXML := `
-
- Hello world !
- Contenu2
- `
+ rootXML := `
+ Hello world !
+ Contenu2
+ Contenu3
+ Contenu4
+ Contenu5
+`
var root *xixo.XMLElement
parser := xixo.NewXMLParser(bytes.NewBufferString(rootXML), io.Discard).EnableXpath()
parser.RegisterCallback("root", func(x *xixo.XMLElement) (*xixo.XMLElement, error) {
root = x
+ assert.Equal(t, root.InnerText, "\n")
return x, nil
})
@@ -104,23 +83,18 @@ func TestElementStringShouldReturnXML(t *testing.T) {
err := parser.Stream()
assert.Nil(t, err)
- expected := `
- Hello world !
- Contenu2
-`
-
- assert.Equal(t, expected, root.String())
+ assert.Equal(t, rootXML, root.String())
}
-func TestElementStringShouldReturnXMLWithSameOrder(t *testing.T) {
+func TestElementStringShouldPreserverContentOrder(t *testing.T) {
t.Parallel()
rootXML := `
Hello world !
Contenu2
- Contenu2
- Contenu2
- Contenu2
+ Contenu3
+ Contenu4
+ Contenu5
`
var root *xixo.XMLElement
@@ -128,6 +102,7 @@ func TestElementStringShouldReturnXMLWithSameOrder(t *testing.T) {
parser := xixo.NewXMLParser(bytes.NewBufferString(rootXML), io.Discard).EnableXpath()
parser.RegisterCallback("root", func(x *xixo.XMLElement) (*xixo.XMLElement, error) {
root = x
+ assert.Equal(t, root.InnerText, "\n")
return x, nil
})
diff --git a/pkg/xixo/parser.go b/pkg/xixo/parser.go
index 7f55aa0..50c81be 100644
--- a/pkg/xixo/parser.go
+++ b/pkg/xixo/parser.go
@@ -22,6 +22,7 @@ type XMLParser struct {
scratch *scratch
scratch2 *scratch
scratchWriter *scratch
+ scratchOuterText *scratch
deffer bool
TotalReadSize uint64
nextWrite *byte
@@ -37,6 +38,7 @@ func NewXMLParser(reader io.Reader, writer io.Writer) *XMLParser {
scratch: &scratch{data: make([]byte, 1024)},
scratch2: &scratch{data: make([]byte, 1024)},
scratchWriter: &scratch{data: make([]byte, 1024)},
+ scratchOuterText: &scratch{data: make([]byte, 1024)},
}
}
@@ -145,7 +147,7 @@ func (x *XMLParser) parse() error {
continue
}
- iscomment, err = x.isComment()
+ iscomment, _, err = x.readComment()
if err != nil {
return err
@@ -170,6 +172,9 @@ func (x *XMLParser) parse() error {
return err
}
+ x.scratch2.reset()
+ x.scratchOuterText.reset()
+
continue
}
@@ -226,8 +231,11 @@ func (x *XMLParser) getElementTree(result *XMLElement) *XMLElement {
element *XMLElement
tagClosed bool
iscomment bool
+ comment CommentElement
)
+ result.outerTextBefore = string(x.scratchOuterText.bytes())
+ x.scratchOuterText.reset()
x.scratch2.reset() // this hold the inner text
for {
@@ -255,7 +263,7 @@ func (x *XMLParser) getElementTree(result *XMLElement) *XMLElement {
continue
}
- iscomment, err = x.isComment()
+ iscomment, comment, err = x.readComment()
if err != nil {
result.Err = err
@@ -264,6 +272,8 @@ func (x *XMLParser) getElementTree(result *XMLElement) *XMLElement {
}
if iscomment {
+ result.AddComment(comment)
+
continue
}
@@ -284,9 +294,8 @@ func (x *XMLParser) getElementTree(result *XMLElement) *XMLElement {
}
if tag == result.Name {
- if len(result.Childs) == 0 {
- result.InnerText = string(x.scratch2.bytes())
- }
+ result.InnerText = string(x.scratch2.bytes())
+ x.scratch2.reset()
return result
}
@@ -344,6 +353,7 @@ func (x *XMLParser) getElementTree(result *XMLElement) *XMLElement {
}
} else {
x.scratch2.add(cur)
+ x.scratchOuterText.add(cur)
}
}
}
@@ -489,9 +499,7 @@ search_close_tag:
return nil, false, x.defaultError()
}
result.AddAttribute(attr, attrVal)
- // if x.xpathEnabled {
- // result.attrs = append(result.attrs, &xmlAttr{name: attr, value: attrVal})
- // }
+
x.scratch.reset()
continue
@@ -510,25 +518,26 @@ search_close_tag:
}
}
-func (x *XMLParser) isComment() (bool, error) {
+func (x *XMLParser) readComment() (bool, CommentElement, error) {
var (
- c byte
- err error
+ c byte
+ err error
+ result CommentElement
)
c, err = x.readByte()
if err != nil {
- return false, err
+ return false, result, err
}
if c != '!' {
err := x.unreadByte()
if err != nil {
- return false, err
+ return false, result, err
}
- return false, nil
+ return false, result, nil
}
var d, e byte
@@ -536,19 +545,19 @@ func (x *XMLParser) isComment() (bool, error) {
d, err = x.readByte()
if err != nil {
- return false, err
+ return false, result, err
}
e, err = x.readByte()
if err != nil {
- return false, err
+ return false, result, err
}
if d != '-' || e != '-' {
err = x.defaultError()
- return false, err
+ return false, result, err
}
// skip part
@@ -558,14 +567,23 @@ func (x *XMLParser) isComment() (bool, error) {
c, err = x.readByte()
if err != nil {
- return false, err
+ return false, result, err
}
if c == '>' &&
len(x.scratch.bytes()) > 1 &&
x.scratch.bytes()[len(x.scratch.bytes())-1] == '-' &&
x.scratch.bytes()[len(x.scratch.bytes())-2] == '-' {
- return true, nil
+ result = CommentElement{
+ string(x.scratchOuterText.bytes()),
+ string(x.scratch.bytes())[:len(x.scratch.bytes())-2],
+ }
+
+ x.scratchOuterText.reset()
+ x.scratch2.reset()
+ x.scratch.reset()
+
+ return true, result, nil
}
x.scratch.add(c)
@@ -817,6 +835,7 @@ skipDecleration:
func (x *XMLParser) closeTagName() (string, error) {
x.scratch.reset()
+ x.scratchOuterText.reset()
var (
c byte
diff --git a/pkg/xixo/parser_test.go b/pkg/xixo/parser_test.go
index 1042cca..92c3f89 100644
--- a/pkg/xixo/parser_test.go
+++ b/pkg/xixo/parser_test.go
@@ -159,8 +159,7 @@ func TestAttributsShouldSavedAfterParser(t *testing.T) {
func TestModifyAttributsWithMapCallback(t *testing.T) {
t.Parallel()
// Fichier XML en entrée
- inputXML := `
-
+ inputXML := `
Hello world!
Contenu2 !
`
@@ -180,18 +179,15 @@ func TestModifyAttributsWithMapCallback(t *testing.T) {
assert.Nil(t, err)
// Résultat XML attendu avec le contenu modifié
- expectedResultXML := `
-
- newChildContent
- Contenu2 !
-`
+ expectedResultXML := `
+ newChildContent
+ Contenu2 !
+ `
// Vérifiez si le résultat XML correspond à l'attendu
resultXML := resultXMLBuffer.String()
- if resultXML != expectedResultXML {
- t.Errorf("Le résultat XML ne correspond pas à l'attendu.\nAttendu:\n%s\nObtenu:\n%s", expectedResultXML, resultXML)
- }
+ assert.Equal(t, expectedResultXML, resultXML)
}
func TestAttributsWithMapCallbackIsInDictionary(t *testing.T) {
@@ -233,9 +229,35 @@ func TestStreamWithoutModifications(t *testing.T) {
input string
element string
}{
+ // How to replace an array of sub-elements
+ // {input: "icb1jcb2kcb3l", element: "a"},
+ // {input: "icb1jcb2k", element: "a"},
+
+ {input: "icb1jcb2k", element: "a"},
+
{input: "", element: "a"},
{input: "", element: "a"},
+ {input: "", element: "z"},
+ {input: "innerZ", element: "b"},
+ {input: "innerA", element: "a"},
+ {input: " ", element: "b"},
+ {input: " ", element: "a"},
+
{input: "", element: "b"},
+ {input: " ", element: "b"},
+ {input: " ", element: "a"},
+ {input: " ", element: "b"},
+ // {input: " ", element: "a"},
+ // {input: "i", element: "a"},
+
+ {input: "", element: "a"},
+ {input: "", element: "a"},
+
+ {input: "i", element: "a"},
+ {input: "ij", element: "a"},
+
+ {input: "ijk", element: "a"},
+ {input: "ijk", element: "a"},
// {input: "", element: "b"},
{input: "", element: "b"},
}
@@ -269,3 +291,37 @@ func TestStreamWithoutModifications(t *testing.T) {
assert.Equal(t, expectedResultXML, resultXML)
}
}
+
+func TestModifyShouldPreserveIndentForElementInline(t *testing.T) {
+ t.Parallel()
+ // Fichier XML en entrée
+ inputXML := `
+
+youenHello world!
+ Contenu2 !
+ `
+
+ // Lisez les résultats du canal et construisez le XML résultant
+ var resultXMLBuffer bytes.Buffer
+
+ // Créez un bufio.Reader à partir du XML en entrée
+ reader := bytes.NewBufferString(inputXML)
+
+ // Créez une nouvelle instance du parser XML avec la fonction de rappel
+ parser := xixo.NewXMLParser(reader, &resultXMLBuffer).EnableXpath()
+ parser.RegisterMapCallback("root", func(m map[string]string) (map[string]string, error) {
+ return m, nil
+ })
+
+ // Créez un canal pour collecter les résultats du parser
+ err := parser.Stream()
+ assert.Nil(t, err)
+
+ // Résultat XML attendu avec le contenu modifié
+ expectedResultXML := inputXML
+
+ // Vérifiez si le résultat XML correspond à l'attendu
+ resultXML := resultXMLBuffer.String()
+
+ assert.Equal(t, expectedResultXML, resultXML)
+}