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

Having issue while convertnig xml with single element to json array or vice versa #330

Closed
rvashishth opened this issue Mar 20, 2017 · 25 comments

Comments

@rvashishth
Copy link

rvashishth commented Mar 20, 2017

Please have a look at -
http://stackoverflow.com/questions/42900503/json-xml-converter-having-array-in-json-string/42901710#42901710

http://heshans.blogspot.in/2014/01/java-library-to-convert-xml-to-json.html

I am getting json array for below xml -

<readResult><errors><code>400</code></errors><errors><code>402</code></errors></readResult>
{"readResult":{"errors":[{"code":400},{"code":402}]}}

but, we need similar behavior while converting json to xml and back again where json array is having single element i.e.

input json - {"readResult": {"errors": [{"code": 400}]}}
converted xml - <readResult><errors><code>400</code></errors></readResult>
output json - {"readResult":{"errors":{"code":400}}}
@johnjaylward
Copy link
Contributor

The JSONML class has better reversibility than the XML class. Have you tried using that one?

@rvashishth
Copy link
Author

Not yet. i'll get back once i am done doing that. Thanks

@rvashishth
Copy link
Author

I couldn't solve this. My issue is to convert a json object having array with single element when converted to xml, should indicated if that is an array/list instead of simple xml element. This api works fine when i have more then one entry in json array. But when i have only one entry in json array it don't.

@rvashishth
Copy link
Author

It looks like issue is with line number 603,604 in XML.java class. Here we are checking if element is an array. If it is an array, there is again a loop in which we again check each element of the array. Now if array is having only one element. It will not pass the second if block.

@rvashishth
Copy link
Author

I could see in the XML.toString(Object,tagName) we have handled array and when [ symbol appeared in json string we adde tag "array" in generated xml string. But while converting XML back to Json this array tag is not handled.

@johnjaylward
Copy link
Contributor

@rvashishth , could you share the parts of your code that are doing the conversions to and from XML?

@rvashishth
Copy link
Author

	public static void main(String[] args) {
		String json = "{\"readResult\":{\"errors\":[{\"code\":\"400\"}]}}";
		System.out.println(json); // 1. {"readResult":{"errors":[{"code":"400"}]}}
		JSONObject jsonObject = new JSONObject(json);
		System.out.println(jsonObject.toString()); // 2. {"readResult":{"errors":[{"code":"400"}]}}
		String xml = XML.toString(jsonObject);
		System.out.println(xml); // 3. <readResult><errors><code>400</code></errors></readResult>
		json = XML.toJSONObject(xml).toString();
		System.out.println(json); // 4. {"readResult":{"errors":{"code":400}}}
	}

Here at point 4, resulted json doesn't have array. Our requirement is to get an json -> convert to xml -> process it -> convert back to json -> reply the new or original json.

It works fine when we have more then one element in json array it works fine.

@johnjaylward
Copy link
Contributor

johnjaylward commented Mar 31, 2017

Please try the JSONML class. It's much better at reversability than the XML class. The XML will be a different format, but you should be able to get back the same structure you put in.

public static void main(String[] args) {
	String json = "{\"readResult\":{\"errors\":[{\"code\":\"400\"}]}}";
	JSONObject jsonObject = new JSONObject(json);
	String xml = JSONML.toString(jsonObject);
	System.out.println(xml);
	String newJson = JSONML.toJSONObject(xml).toString();
	System.out.println(newJson);
	assert jsonObject.similar(new JSONObject(newJson)) : "Objects not similar";
}

@rvashishth
Copy link
Author

Above example resulted in following exception

{"readResult":{"errors":[{"code":"400"}]}} Exception in thread "main" org.json.JSONException: Empty string. at org.json.XML.noSpace(XML.java:118) at org.json.JSONML.toString(JSONML.java:498) at JsonXMLConvertor.main(JsonXMLConvertor.java:24)

EmptyString error, something to do with tagName key in JSONML.toString method. Also i couldn't see latest build on maven repo. Please help

@johnjaylward
Copy link
Contributor

Thanks, I see the error occurring too in the latest master. I'll take a look and see if I can mitigate it.

@johnjaylward
Copy link
Contributor

ah, I see. the toString method is expecting the JSONObject to be a specific form up front. The reality is that the JSONML format is mainly used when you start with XML, not JSON.

So, to use JSONML with your example, we'd have to start with your format:

<readResult><errors><code>400</code></errors><errors><code>402</code></errors></readResult>

Then when you use the JSONML.toJSONObject(xml,false) call you'd get this:

{"tagName":"readResult", "childNodes":[
    {"tagName":"errors","childNodes":[{"childNodes":[400],"tagName":"code"}]},
    {"tagName":"errors","childNodes":[{"childNodes":[402],"tagName":"code"}]}
]}

JSONML is really an XML representation in JSON. but I think you are looking for the reverse, a JSON representation in XML.

This probably won't work for you as the JSON there is pretty ugly.

@johnjaylward
Copy link
Contributor

johnjaylward commented Apr 3, 2017

@rvashishth I just realized that our JSONML class has 2 different options for representation. The "Array" representation is much more compact and looks a lot nicer. It also maintains document order and structure better than the "Object" form I used in the code above. Here's what the array form would look like:

JSONML.toJSONArray(originalXml, true);

Output:

["readResult",["errors",{"someAttr":"arrtValue"},["code",400]],["errors",["code",402]]]

Not sure if that JSONArray structure will work for you, but it's a heck of a lot prettier than the JSONObject notation for JSONML.

@stleary
Copy link
Owner

stleary commented Apr 4, 2017

@johnjaylward have you determined whether there is a bug to fix, user error, or is this just a limitation of the design?

@johnjaylward
Copy link
Contributor

johnjaylward commented Apr 4, 2017

looks like a limitation of design. If the user was starting with XML, then JSONML would work I think, but when starting with JSON, it seems there isn't really a good conversion to XML in order to get the JSON back. I was writing some test cases but even the JSONML isn't 100% reversible because the xml element portions of the tags are stored in an object, which is unordered. input may be <tag el1="value" el2="val"/> and once read and rewritten, may end up as <tag el2="val" el1="value"/>

Our XML processor being very generic and not 100% reversible is a long standing known issue.

@azngeek
Copy link

azngeek commented Apr 10, 2017

I have the same issue. I only need to convert from xml to json. If the element contains a children node then the children node MUST be treated as an array. Currently this is not the case.

B will be converted into an object. This does not seem to be a clever idea as the document structure tells you that it is an array but not an object. So having the ability to tread all children nodes as an array would be very good.

<a>
 <b></b
</a>

The Bs are converted into an array. That is the correct way and should be done exactly in the same way like for the upper case

<a>
 <b></b
 <b></b
</a>

@johnjaylward
Copy link
Contributor

@azngeek , I don't have an aversion to a pull request that would offer that, but I think a good case for why that is better than the JSONML.toJSONArray() method would have to be made. That function already reads the XML into an array format.

@azngeek
Copy link

azngeek commented Apr 10, 2017

@johnjaylward what i need is a json structure like that one for any case where a/b can contain one or more entries:

{
a: [
{}, // b1
{}, // bn
]
}

The JSONML format does not create a normal JSON structure. So what would be the alternative to create a format like that one?

@johnjaylward
Copy link
Contributor

I believe the JSONML structure can be searched using the JSONPointer notation. I agree it's not that pretty to look at though and not very normative. Your only real guarantee is that the tag name is the first element in the array.

to get a regular structure like you are asking, we'd likely need a secondary flag on the XML.toJSONObject method to indicate that we want all sub element structures to be placed in an array. We would need to maintain the old method signatures as well for backwards compatibility.

then here: https://github.com/stleary/JSON-java/blob/master/XML.java#L403

We would need to change the calls from accumulate to append if the new flag is set for all three cases of accumulate.

Personally, I am not a fan of adding tons of options to the parameter list. It may be time we considered using a configuration object instead like this:

public class XMLConfiguration {
    public boolean keepStrings = false;
    public boolean forceArraySubElements = false;

    public XMLConfiguration(){}

    public XMLConfiguration(boolean keepStrings, boolean forceArraySubElements){
        this.keepStrings  = keepStrings;
        this.forceArraySubElements = forceArraySubElements;
    }
}

@azngeek
Copy link

azngeek commented Apr 10, 2017

I understand. However i have now tested the "JSONML.toJSONArray()" method and i also do not see how it keeps the structural information of the original xml.

Example:

    <a>
        <b>
            <sku>15B-12345</sku>
            <name>
                <de_DE>German Text</de_DE>
                <en_US>English Text</en_US>
            </name>
</b>
</a>

This will result in something like this one:

 [
    "a",
    [
      "b",
      [
        "sku",
        "15B-12345"
      ],
      [
        "name",
        [
          "de_DE",
          "German Text"
        ],
        [
          "en_US",
          "English Text"
        ]
      ]
  ]
]

So this implementation also does not keep the original format as it treats the node a/b as ab object as well but just with the array notation. I would instead a/b/ to be an array which contains an array for each item. So "JsonML (JSON Markup Language) is an application of the JSON (JavaScript Object Notation) format. The purpose of JsonML is to provide a compact format for transporting XML-based markup as JSON which allows it to be losslessly converted back to its original form." Did i miss something?

@johnjaylward
Copy link
Contributor

johnjaylward commented Apr 10, 2017 via email

@stleary
Copy link
Owner

stleary commented Jun 8, 2017

Closing on the presumption that the OP found some type of solution. If anyone thinks this issue still needs action, post here and I will reopen.

@stleary stleary closed this as completed Jun 8, 2017
@shaileshntikhe
Copy link

shaileshntikhe commented Feb 27, 2018

I am facing this issue:

Following is my XML:

<thumbsURL>
  <thumbURL>urlA</thumbURL>
</thumbsURL>

Equivalent JSON is: {"thumbsURL": { "thumbURL": "urlA" }}
If I update my XML to following:

<thumbsURL>
  <thumbURL>urlA</thumbURL>
  <thumbURL>urlB</thumbURL>
</thumbsURL>

Equivalent JSON is: { "thumbsURL": {"thumbURL": ["urlA", "urlB"] }}

Is there any generic way that I'll just specify XML string and single child values should be converted into json-array. I could find several links where it is specified that if we mark(for eg: <?xml-multiple array?>) certain child elements, XML to JSON conversion converts single element into JSON array.

@stleary : can you please reopen this issue?

@johnjaylward
Copy link
Contributor

The XML parser in this project is very simple. The way to handle it would be on the JSON side:

if (root.opt("node") instanceof JSONArray) {
  // work with array
} else {
  // work with null or non-array
}

or

if (root.optJSONArray("node") != null) {
  // work with array
} else {
  // work with null or non-array
}

I can't see us handling this differently unless we assumed every XML element was an array and used append instead of accumulate

@dpb519
Copy link

dpb519 commented Jul 24, 2018

use the below link to achieve this. Its basically adding an array attribute to the node/element you want to be treated as an array

https://www.newtonsoft.com/json/help/html/ConvertingJSONandXML.htm

@Boy2ang
Copy link

Boy2ang commented Jun 17, 2020

哪位老哥知道了快救救我!

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

No branches or pull requests

7 participants