Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible to force array? #23

Open
gbminnock opened this issue May 27, 2019 · 4 comments
Open

Possible to force array? #23

gbminnock opened this issue May 27, 2019 · 4 comments

Comments

@gbminnock
Copy link

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

@basgys
Copy link
Owner

basgys commented Jun 20, 2019

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.

@riccardomanfrin
Copy link

riccardomanfrin commented Aug 1, 2019

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

@riccardomanfrin
Copy link

riccardomanfrin commented Aug 1, 2019

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)

riccardomanfrin added a commit to riccardomanfrin/goxml2json that referenced this issue Aug 1, 2019
riccardomanfrin added a commit to riccardomanfrin/goxml2json that referenced this issue Aug 1, 2019
riccardomanfrin added a commit to riccardomanfrin/goxml2json that referenced this issue Aug 1, 2019
@javadev
Copy link

javadev commented Feb 16, 2020

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"
            ]
          }
        }
      ]
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants