Skip to content

Commit

Permalink
feat: preserve simple quote
Browse files Browse the repository at this point in the history
  • Loading branch information
Youen Péron committed Nov 16, 2023
1 parent 9f72027 commit 52e31f5
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 266 deletions.
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand All @@ -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=
Expand Down
9 changes: 4 additions & 5 deletions pkg/xixo/calback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
"<root>\n\t<element1>newChildContent</element1>\n\t<element2>Contenu2 </element2>\n</root>",
editedRoot.String(),
)
}

func badJSONCallback(source string) (string, error) {
Expand Down
23 changes: 8 additions & 15 deletions pkg/xixo/callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)

Expand All @@ -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)
}
}
}
Expand All @@ -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
}
}

Expand Down
64 changes: 39 additions & 25 deletions pkg/xixo/element.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -16,29 +45,13 @@ type XMLElement struct {
// filled when xpath enabled
childs []*XMLElement
parent *XMLElement
attrs []*xmlAttr
localName string
prefix string

outerTextBefore string
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
Expand Down Expand Up @@ -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, " ")
Expand All @@ -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: "",
}
Expand Down
12 changes: 7 additions & 5 deletions pkg/xixo/element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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 := "<root foo=\"bar\">Hello</root>"
assert.Equal(t, expected, root.String())
Expand All @@ -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 := "<root foo=\"bar\">Hello</root>"
assert.Equal(t, expected, root.String())
root.AddAttribute("foo", "bas")
root.AddAttribute(xixo.Attribute{"foo", "bas", xixo.DoubleQuotes})

expected = "<root foo=\"bas\">Hello</root>"
assert.Equal(t, expected, root.String())
Expand Down
3 changes: 2 additions & 1 deletion pkg/xixo/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
13 changes: 13 additions & 0 deletions pkg/xixo/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,19 @@ func TestStreamWithoutModifications(t *testing.T) {
// {input: "<a>i<b>cb1</b>j<b>cb2</b>k<b>cb3</b>l</a>", element: "a"},
// {input: "<a>i<b>cb1</b>j<b>cb2</b>k</a>", element: "a"},

{input: "<a><b/>\n</a>", element: "a"},
{input: "<a><b/>\n</a>", element: "b"},

{input: "<a><b/>\n<c/>\n</a>", element: "a"},
{input: "<a><b/>\n<c/>\n</a>", element: "b"},
{input: "<a><b/>\n<c/>\n</a>", element: "c"},

{input: "<a><b attr='true'/>\n</a>", element: "a"},
{input: "<a><b attr=\"true\"/>\n</a>", element: "a"},

{input: "<a><b attr='true'/>\n</a>", element: "b"},
{input: "<a><b attr=\"true\"/>\n</a>", element: "b"},

{input: "<a>i<b>cb1</b>j<c>cb2</c>k</a>", element: "a"},

{input: "<a/>", element: "a"},
Expand Down
Loading

0 comments on commit 52e31f5

Please sign in to comment.