goxml2json
goxml2json copied to clipboard
Possible to force array?
Hi, is it possible for force an array? For example i have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<rss>
<channel>
<item>
<apps>
<app>App One</app>
</apps>
</item>
<item>
<apps>
<app>App Two</app>
<app>App Three</app>
</apps>
</item>
</channel>
</rss>
which converts to
{
"rss": {
"channel": {
"item": [
{ "apps": { "app": "App One" } },
{ "apps": { "app": ["App Two", "App Three"] } }
]
}
}
}
The problem is that the Apps/app node is an object but the second & third Apps/app node is an array. I understand why given the structure, but is it possible to force the first node to also be an array? Tks, Gary
Hi Gary,
I am really sorry for the delay. I haven't had much time to dedicate to this project lately.
Yes someone implemented this functionality on PR #16.
I think you can do something like that:
xml := strings.NewReader(`...`)
json, err := xj.Convert(
xml,
xj.WithNodes(
xj.NodePlugin("rss.channel.item.apps.app", xj.ToArray()),
),
)
Let me know if it works.
xj.NodePlugin("rss.channel.item.apps.app", xj.ToArray()),
By trying, it looks to me that the way it works is by addressing the parent object holding the array, that is
xj.NodePlugin("rss.channel.item.apps", xj.ToArray()),
The problem with this is when the parent object also has attributes.. this is not rendered correctly; you can check by this test case:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"strings"
xj "github.com/basgys/goxml2json"
)
func X2J(value string) string {
var xml []byte
xml = []byte(value)
xmlrd := strings.NewReader(string(xml))
jsonbytes, err := xj.Convert(xmlrd, xj.WithNodes(
xj.NodePlugin("foo.bar", xj.ToArray()),
))
if err != nil {
log.Fatal(err)
return ""
}
return jsonbytes.String()
}
func main() {
xml := `<foo>
<bar barname="bar">
<zoo name="zoo"/>
</bar>
<bar barname="bar">
<zoo name="zoo"/>
<zoo name="zoo"/>
</bar>
</foo>`
fmt.Printf("XML:\n%v\n", xml)
rawjson := X2J(xml)
var prettyjson bytes.Buffer
json.Indent(&prettyjson, []byte(rawjson), "", " ")
fmt.Printf("JSON:\n%v\n", prettyjson.String())
}
In this test, zoo
is indeed forced into an array, but, because bar also has attributes (barname
), those attributes get trapped in the forced translation as well:(
is there any other way to avoid this?
The expected result I would like is the following:
{
"foo": {
"bar": [
{
"zoo": [
{
"-name": "zoo"
}
],
"-barname": "bar"
},
{
"zoo": [
{
"-name": "zoo"
},
{
"-name": "zoo"
}
],
"-barname": "bar"
}
]
}
}
whereas I currently get this:
{
"foo": {
"bar": [
{
"zoo": [
{
"-name": "zoo"
}
],
"-barname": [
"bar"
]
},
{
"zoo": [
{
"-name": "zoo"
},
{
"-name": "zoo"
}
],
"-barname": "bar"
}
]
}
}
As you see, although zoo becomes an array, where the transformation is applied, also the attributes of bar are translated hence screwing things up
I think I'm figuring it out, the ChildrenAlwaysAsArray
property only gets applied to the first found path, not all paths. So in my example, the presence of repeated bar items is not addressed and only the first object of the bar array is found with ChildrenAlwaysAsArray
set to true
I also think that a better approach would be to not indicate the parent whose children must be arrays, but instead the children that are array objects. This way you could selectively discriminate between properties and items (and items of different types)
It may be an attribute array="true"
to force element as array.
<?xml version="1.0" encoding="UTF-8"?>
<rss>
<channel>
<item>
<apps>
<app array="true">App One</app>
</apps>
</item>
<item>
<apps>
<app>App Two</app>
<app>App Three</app>
</apps>
</item>
</channel>
</rss>
{
"rss": {
"channel": {
"item": [
{
"apps": {
"app": [
"App One"
]
}
},
{
"apps": {
"app": [
"App Two",
"App Three"
]
}
}
]
}
}
}