41

I am designing an API for my webapp.

I was thinking to support only JSON responses (not XML) because more streamlined.

But I have just bumped to this XML:

<folders>
    <folder id="123" private="0" archived="0" order="1">Shopping</folder>
</folders>

and I was wondering how the corresponding JSON would be. I have the feeling, in this case, XML would be more compact.

Machavity
  • 30,841
  • 27
  • 92
  • 100
Dan
  • 15,948
  • 20
  • 63
  • 92

13 Answers13

26

Perhaps:

{
  "folders": [
    { "id":123, "private":0, "archived":0, "order":1, "title":"Shopping" },
    ...
  ]
}

Because there is not an exact correspondence between XML and JSON, you are free (e.g. have to define) how the two data-structures map. For instance, in the above, the "folder" element is implicit in the nested objects in the "folders" array.

This could be expanded as in:

"folders": [{"folder": { .... }]

Etc, but there is still the problem of not being able to capture content+attributes as consistently as XML. In any case, your data-structure -> JSON|XML serializer likely works in a particular way (and please, please, use a library, not "hand-rolled" JSON-string-munging). That is; the format of the XML and JSON should be uniformly dictated (somehow) by the data-structure for transmission.

  • 2
    +1 I do like this suggestion, but you do lose the fact (if it had any significance before anyway) that these values are actually attributes and not key / value pairs. – Doug Molineux May 22 '12 at 21:19
  • 2
    { "folders": [ { "attributes" : { "id":123, "private":0, "archived":0, "order":1, }, "value":"Shopping" } ... ] } – noderman Jul 25 '14 at 00:16
  • 3
    I'm surprised that this answer has so many likes, because it's wrong. This json is equivalent to xml: ` 123 0 0 1 Shopping ` But not what was asked by ts. – Supervision Aug 25 '15 at 13:00
  • 3
    @Supervision, actually it is not. The question is asking for a compact equivalent. You are assuming a bijection is necessary, as shown in your json->xml answer. The question is specifically saying it wants to support json and not xml, so he wants a xml->json conversion and does not care if it is lossy. – Nick Freeman Sep 24 '15 at 12:52
  • +1 "perhaps" and "no exact" hit the nail on the head; and for a suggestion that is compact and json-style, even if it would not reverse-translate back to the same XML (not a requirement of the OP). – FlyingSheep Aug 14 '18 at 18:54
20

This approach supports inverse transformation to XML:

{
    "folders": {
        "folder":{ 
        "@": {
            "id": "123",
            "private": "0",
            "archived": "0",
            "order": "1"
            },
        "#": "Shopping"
        }
    }
}

It works correctly with js2xmlparser.

Supervision
  • 1,683
  • 1
  • 18
  • 23
  • 4
    yes and no. While a valid JSON, and one that would translate back to the original XML (not an explicit requirement of this question), it is a JSON that stinks of XML and is NOT compact (a requirement of the question). If we were to model this data directly as JSON without reference to XML, we would likely chose something along the lines of the accepted answer. As JSON has no concept of elements or attributes (and XML no concept of arrays) all translations to and from XML will be defacto imperfect and a matter of personal preference and exact use-case. – FlyingSheep Aug 14 '18 at 18:50
  • 3
    ... though reversibility back to the original XML maybe a requirement of others finding this question, hence no down-vote. Maybe "You need ..." should be "How about ...?" – FlyingSheep Aug 14 '18 at 18:59
10

There is a JSON notation / convention called badgerfish attempts to standardizes (at least its own terms) the way of preserving of most of the low level XML semantics when XML DOM represented as JSON DOM (with attributes of course) (see http://badgerfish.ning.com/).

So you can easily convert back the badgerfishied-json representation to the XML representation and you still work on the structure with your favorite XML toolset (for me its XPATH / QUERY expressions and tools).

It has also easy to memorize syntax rules (total 9) like: "Attributes go in properties whose names begin with @". You can work on badgerfishied-json in the text editor without overloading your neural circuitry unnecessarily. Usually you can memorize them in the first pass.

underscore
  • 752
  • 12
  • 9
  • 4
    There are multiple other notation / conventions as well. See http://wiki.open311.org/JSON_and_XML_Conversion/ Plus I've come across one that is a mixture of some of those – Dijkgraaf Aug 14 '16 at 22:00
  • badgerfish *appears* to discard order based on the examples at that link (e.g. `
    a
    b
    c
    ` becomes `"div": [{"$": "a", "$": "c"}], "span": {"$": "b"}`), which makes it unfit for round tripping .
    – Dwayne Robinson Jul 09 '21 at 03:37
8

An example of how YQL presents XML and the corresponding JSON. No need to know anything about YQL to understand this but if you are interested your can check the YQL console and try it out yourself in the YQL console

XML

<results>
    <a href="/">NBA</a>
    <a class="topnav" href="#">TEAMS</a>
    <a href="/teams/">Teams</a>
    <a href="/hawks/">Atlanta</a>

JSON

"results": {
  "a": [
    {
     "href": "/",
     "content": "NBA"
    },
    {
     "class": "topnav",
     "href": "#",
     "content": "TEAMS"
    },
    {
     "href": "/teams/",
     "content": "Teams"
    },
    {
     "href": "/hawks/",
     "content": "Atlanta"
    },
spier
  • 2,642
  • 1
  • 19
  • 16
7

It seems to me that the most exact correspondence between XML and JSON would need to represent an XML node as a triplet (i.e. array): [name, attributes, value], with name being a string, attributes an object with attribute names as keys and attribute values as (string) values, and value a string (for atomic values) or an array of such triplets.

By such mapping the JSON-equivalent of

<folders>
    <folder id="123" private="0" archived="0" order="1">Shopping</folder>
</folders>

would be

[  "folders",
   {}, 
   [
      [  "folder", 
         {   "id": "123",
             "private": "0",
             "archived": "0",
             "order": "1"
         },
         "Shopping"
      ]
   ]
]

Actually the idea behind this mapping is that:

1) XML-JSON transformation be reversible. 2) The "sibling" relationship of sub-nodes be preserved

At the same time the distinction between attribute nodes and value nodes is explicit here.

Does it make sense? And does it justify the complexity overhead?

Aram Gharib
  • 81
  • 1
  • 4
  • Did you come up with this? – Koray Tugay May 01 '18 at 00:20
  • @KorayTugay Sorry for this laaaaate reply!... Yes, I mean, I never went further to implement or use such transformation. But I had already "imagined" that if an interface had to work interchangeably with JSON and XML, the rules of transformation should look like this. – Aram Gharib Sep 23 '18 at 08:43
  • @Gavin Sorry for this late (and probably out of date!) reaction!... You are right!... And probably because preserving the logical correspondence implies such a structure... One difference I found was the way JsonML defines `element-list`: the elements of the list seem to be at the same level as `tag-name` and `attributes` tokens, whilst in my suggestion an `element` is always a **triplet**- i.e. an array of three elements; the third element being a string literal or another element (triplet)... – Aram Gharib Sep 23 '18 at 08:54
  • @Gavin I rectify the end of the last sentence: "... the third element being a string literal or an array of `element`s (array of triplets)" – Aram Gharib Sep 24 '18 at 21:15
3

Could be compact in the JSON too, attribute is just the same as the value inside tag

from here:

http://www.json.org/example.html

{"widget": {
    "debug": "on",
    "window": {
        "title": "Sample Konfabulator Widget",
        "name": "main_window",
        "width": 500,
        "height": 500
    },
    "image": { 
        "src": "Images/Sun.png",
        "name": "sun1",
        "hOffset": 250,
        "vOffset": 250,
        "alignment": "center"
    }
}}  

The same text expressed as XML:

<widget>
    <debug>on</debug>
    <window title="Sample Konfabulator Widget">
        <name>main_window</name>
        <width>500</width>
        <height>500</height>
    </window>
    <image src="Images/Sun.png" name="sun1">
        <hOffset>250</hOffset>
        <vOffset>250</vOffset>
        <alignment>center</alignment>
    </image>
</widget>
Roman Goyenko
  • 6,965
  • 5
  • 48
  • 81
  • 2
    The same text expressed as XML would create all child nodes, not attributes... especially randomly decided attributes. – Nick Oct 14 '16 at 20:01
  • Well, it depends on your implementation of JSON to xml conversion. You can specify what should be converted to attribute and what should be converted to a child node. I don't see why this couldn't be done. – Roman Goyenko Oct 17 '16 at 16:23
  • 2
    True, but a direct conversion without any mapping would take no assumptions... someone who puts that JSON into a converter and expects that XML probably won't get it. I just wanted to mention that more work would be needed. – Nick Oct 17 '16 at 16:43
3

My choice for XML representation, to make sure it's somehow reversible, is as follows (using your example):

    <folders>
        <folder id="123" private="0" archived="0" order="1">Shopping</folder>
    </folders>

translates to:

    {
      "folders": [
        {
          "folder": [
            {
              "@id": 123,
              "@private": 0,
              "@archived": 0,
              "@order": 1,
              "$t": "Shopping"
            }
          ]
        }
      ]
    }

So by using @ as an indicator for "attribute" and $t as indicator for "text content" I can revert the JSON string to a faithful version of the original XML.

A nice Node package to perform this conversion is XML2JSON, although it doesn't do anything special with the attributes, and some extra code is required to produce this output.

2

edit

A similar method is advocated on http://www.jsonml.org/. They coined the term json markup language.


You can pick any mapping you like, but if you map

<el attr="value">
  txt
</el>

to

{"el":{"attr":"value","content":"txt"}}

then how would you map:

<el attr="value" content="txt1">txt2</el>

I'd make use of the fact that some attibute names are forbidden.

{"el":{"attr":"value", "content":"txt1", "":["txt"]}

Or a more compex example:

<widget>
  <debug>on</debug>
  <window title="Sample Konfabulator Widget">
    I just put some text here
    <name>main_window</name>
    <width>500</width>
    <height>500</height>
  </window>
  <image src="Images/Sun.png" name="sun1">
    <hOffset>250<unit>mm</unit></hOffset>
    <vOffset>250</vOffset>
    <alignment>center</alignment>
  </image>
</widget>

could map to:

{"widget":{"":[
  {"debug":{"":["on"]}},
  {"window":{"title":"Sample Konfabulator Widget", "": [
    "I just put some text here",
    {"name":{"":["main window"]}},
    {"width":{"":["500"]}},
    {"height":{"":["500"]}}
  ]},
  {"image":{"src":"Images/Sun.png", "name":"sun1", "":[
    {"hOffset":{"":["250",{"unit":{"":["mm"]}}]}},
    {"vOffset":{"":["250"]}},
    {"alignment":{"":["center"]}}
  }
]}

The rules for this conversion are unambiguous:

  • an element is converted to an object.
  • The object key is the element name.
  • The object value is an object itself, with:
    • an attibute is mapped to an object property. (key/value)
    • the object content is mapped to a special property.
      • The special property name is an empty string. This can never collide with an xml attribyte name.
      • The special property value is an array.
      • Within the array, plain text is represented as a string.
      • Within the array, elements are represented as objects, as described above.

To safe space, there is a way to unambigously simplify mentioned mapping:

{"widget":{"":[
  {"debug":"on"},
  {"window":{"title":"Sample Konfabulator Widget", "": [
    "I just put some text here",
    {"name":"main window"},
    {"width":"500"},
    {"height":"500"}
  ]},
  {"image":{"src":"Images/Sun.png", "name":"sun1", "":[
    {"hOffset":["250",{"unit":"mm"}]},
    {"vOffset":"250"},
    {"alignment":"center"}
  }
]}
  • If an element doesn't have any attibutes, the value object (containing the special empty string mapping to an array) is replaced by the array directly. So instead of:

    {"hOffset":{"":["250",{"unit":{"":["mm"]}}]}}

you get

{"hOffset":["250",{"unit":["mm"]}]}
  • If the element content is just text, the array containing the string value is replaced by the string value directly, so you get:

    {"hOffset":["250",{"unit":"mm"}]}

This way, there will always be exactly one way to map the jml (json markup language) back to xml (or html)

realbart
  • 3,497
  • 1
  • 25
  • 37
  • 1
    May be a bit late, but I like this due to the fact that it supports ordering of items in the context properly. So you can have a string + child (Since it's stuffed in an array). All other answers miss this point. – William Oct 02 '21 at 00:23
1

JSON is more uniform than XML and doesn't distinguish between plain-text attributes and hierarchical contents. The natural representation for your example would be

[
  {"id": 123, "private": 0, "archived": 0, "order": 1, "name": "Shopping"}
]

This is still more compact than the respective XML.

Adam Byrtek
  • 12,011
  • 2
  • 32
  • 32
0

"content" is used for the actual text, while the attributes are its siblings in the resulted JSON:

{ "folders":
 { "folder":
  {
   "archived":0,
   "private":0,
   "id":123,
   "content":"Shopping",
   "order":1
  }
 }
}

Java implementation details

Community
  • 1
  • 1
0

I've ran into a scenario that needs both XML and JSON for input and output based on what was being passed in. I found a way that works with XML Attributes/Properties and JSON. Now note, it's how it coded in Java, makes it work this way.

My XML Example:

<Log>
    <datetime>05/05/2017 13:45:22</datetime>
    <sessionid>2da236d2-3852-4a09-8067-198193d2828b</sessionid>
    <message msgType="Debug">This is my message</message>
</Log>

My JSON Example:

{
    "datetime":"05/05/2017 13:45:22",
    "sessionid":"2da236d2-3852-4a09-8067-198193d2828b",
    "message": {
        "content":"This is a testa",
        "msgType":"Debug"
    }
}

How I made it work via code Log.java:

package log;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JacksonXmlRootElement(localName = "Log")
public class Log {
    @JacksonXmlProperty(localName = "datetime")
    private String datetime;
    @JacksonXmlProperty(localName = "sessionid")
    private String sessionid;
    @JacksonXmlProperty(localName = "message")
    private Message message;

    public Log() {
        this.sessionid = "0";
        this.datetime = "";
        this.message = new Message();
    }

    public String getDatetime() {
        return datetime;
    }

    public void setDatetime(String datetime) {
        this.datetime = datetime;
    }

    public String getSessionid() {
        return sessionid;
    }

    public void setSessionid(String sessionid) {
        this.sessionid = sessionid;
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }
}

Message.java, Note the @JacksonXmlText below, which is key:

package log;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;

public class Message {
    @JacksonXmlProperty(localName = "msgType", isAttribute = true)
    private String msgType;
    @JacksonXmlText
    private String content;

    public Message() {
        this.content = "";
    }

    public String getMsgType() {
        return msgType;
    }

    public void setMsgType(String msgType) {
        switch(msgType.toLowerCase())
        {
        case "test":
        case "debug":
        case "warn":
        case "error":
            break;
        default:
            msgType = "Unknown";
            break;
        }

        this.msgType = msgType;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

Caller within LogController.java:

..
@RequestMapping(value = "/Logger", produces={"application/xml", "application/json"}, consumes={"application/xml", "application/json"})
public ResponseEntity<String> Logger(@RequestBody String logInfo, @RequestHeader("Content-Type") String contentType) {
    try 
    {
        String xml = "";
        Log logObj = null; 
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add("Content-Type", contentType);

        if (contentType.toLowerCase().contains("json"))
        {
           ObjectMapper mapper = new ObjectMapper();
           logObj = mapper.readValue(logInfo, Log.class);
           xml = mapper.writeValueAsString(logObj);
        }
        else if (contentType.toLowerCase().contains("xml"))
        {
           XmlMapper xmlMapper = new XmlMapper();
           logObj = xmlMapper.readValue(logInfo, Log.class);
           xml = xmlMapper.writeValueAsString(logObj);
        }
        else
           return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);

        //TODO GWL
        //Save Log data, via Async Web Service, Data, or System 
        return new ResponseEntity<String>(xml, responseHeaders, HttpStatus.OK);
    } 
    catch( Exception ex)
    {
        ex.printStackTrace();
        return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
    }
}
Chizl
  • 2,004
  • 17
  • 32
0

Opening caveat: In this answer I am deliberately standing back from the original question, and expanding the scope somewhat. Sometimes to ask the question behind the question we need to look at the big picture first.

Just as all animals are equal, all translations from JSON to XML, and from XML to JSON are unequal. There can be no mathematically correct answers to this question, and even if there are, they may be wrong. All answers will be a matter of opinion, and depend on the exact use-case(s).

JSON and XML both support and omit concepts that make perfect, reversible and native translations difficult.

To name just some:

  • XML requires a root element. JSON does not.

  • XML distinguishes between Elements and Attributes. Attributes are leaves, Elements maybe leaves or branches. JSON objects are close to Elements, but have no direct equivalent to Attributes.

  • JSON has arrays; the nearest XML equivalent is a parent Element, but some translations fall foul where there is only one child element.

  • XML has namespaces….. The less said about those the better…

  • JSON is intended to be compact and lightweight, and is closer (in my humble opinion) to YAML than XML.

Where a concept exists in the source language, but not in the target language, we need to be inventive and creative in our translation. Your use-cases are key.

e.g.

  • Should the result be as native as possible in the target language? Would we model it like this in JSON? Would we model it like this in XML?

  • Should the result be reversible to the original source, even at the expense of “native feel”?

  • Is compactness a criteria? This can be a case for JSON over an element-heavy XML, but not for JSON translated from XML and designed to be translated back.

To make a comparison with human languages: In Swiss German we love our diminutives: Nouns are routinely reduced to the small form where this would be strange in English; but even more bizarrly, we have diminutives of verbs!

So: “wir machen es köchele”, might translate technically to “we will cook littely”, or “we do a little cooking” but either would be poor and incorrect english, and somehow miss the idea.

“we will do a spot of cooking” or “we will have some fun cooking” would be much closer to the original idea. But I suspect that Anthea Bell (of Asterix-to-english translation fame) would truly get the point; “let’s cook a feast….”

Back to the original question. Python programmers have a concept of pythonesque: = most fitting to the core ethos of python. The answer by user166390 (the accepted answer at the time of this answer) strikes me as the most JSONesque.

FlyingSheep
  • 804
  • 1
  • 9
  • 20
-2

Excluding the attribute names that are always same length, JSON will be always more compact as just does not have closing tags ;)

Lorenzo
  • 29,081
  • 49
  • 125
  • 222
  • 2
    There are good reasons for either JSON or XML in different applications (eg JSON utilisation in JavaScript, XML expressive power, etc), but being compact is unlikely to feature... – ChrisV Oct 14 '14 at 16:25
  • Why should compactness not feature? mobile? embedded? Never say never. – FlyingSheep Aug 14 '18 at 21:10