5

I work with a datamodel created using JAXB, from that I can generate XML directly

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0">
<artist-list offset="0" count="1">
   <artist ext:score="100" type="Group" id="4302e264-1cf0-4d1f-aca7-2a6f89e34b36">       
       <name>Farming Incident</name>
       <ipi-list>
          <ipi>1001</ipi>
       </ipi-list>
   </artist>
</artist-list>
</metadata>

and with the help of Jersey also generate JSon using Natural notation

"artist-list":
    {"offset":0,
     "count":1,
     "artist":[
         {"score":"100",
          "type":"Group",
          "id":"4302e264-1cf0-4d1faca7-2a6f89e34b36",
          "name":"Farming Incident",
          "ipi-list":
              {
                  "ipi":[
                       "1001"
                    ]
             }
          }]
     }

The Xml is fine, the json is nearly fine except that because Json directly supports arrays having elements like ipi-list and artist-list doesnt seem very json, is it possible to generate more json like json from my model ?

Additional Information as Requested The json is generated from this MMD schema http://svn.musicbrainz.org/mmd-schema/trunk/brainz-mmd2-jaxb/src/main/resources/musicbrainz_mmd-2.0.xsd using JAXB and Jersey , see http://svn.musicbrainz.org/search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/mmd2/ResultsWriter.java and http://svn.musicbrainz.org/search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/mmd2/ArtistWriter.java

The point is that I want to be able to generate Json and XML from one schema with the minimum of fuss, but apparently the Json doesn't look right so Im looking for a way to improve it (I don't really have any experience of json myself)

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351
  • Is your model originally annotated java classes or a JAXB schema? – beerbajay Jun 07 '12 at 08:32
  • Your output seems a bit odd. Could you show how the classes are annotated and how you convert to XML/JSON? – siebz0r Jun 07 '12 at 12:33
  • It is exactly what you have : for each object you have all its properties and so on. If you don't like it - make your own annotations above existing JAXB ones to produce customized JSON with JSON-specific annotations. – Artem Oboturov Jun 07 '12 at 15:44
  • Does your application should be capable to read the JSON or is it just for exporting purposes ? Because if the schema is different for XML and JSON then you will need to add some logic to your model, especially for the `count` and `offset` of your `artist-list` element. – Alex Jun 08 '12 at 11:21
  • Just output, I need to be able to ouput as xml and json. But dont know what the json output should look like, nor how to get it to look like that. – Paul Taylor Jun 08 '12 at 11:48
  • Does `ipi-list` correspond to an `@XmlElementWrapper`? – bdoughan Jun 12 '12 at 15:19
  • 1
    No its just another XmlElement , but XmlElementWrapper sounds interesting – Paul Taylor Jun 12 '12 at 21:06

5 Answers5

3

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

You could leverage the JSON-Binding and external mapping document in EclipseLink JAXB (MOXy) to support your use case.

External Mapping File (oxml.xml)

You can use the @XmlPath(".") extension in MOXy to flatten parts of your object model. Specify a path of "." tells MOXy to include the referenced object in the parent node.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum10699038">
    <java-types>
        <java-type name="Metadata">
            <java-attributes>
                <xml-element java-attribute="artistList" xml-path="."/>
            </java-attributes>
        </java-type>
        <java-type name="Artist">
            <java-attributes>
                <xml-element java-attribute="ipiList" xml-path="."/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

jaxb.properties

To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain model with the following entry.

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

The code below populates the object model from your XML document, and then marshalled to JSON. It demonstrates how to leverage the external mapping file and put MOXy in JSON mode.

package forum10699038;

import java.io.File;
import java.util.*;
import javax.xml.bind.*;

import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        // READ FROM XML
        JAXBContext jcXML = JAXBContext.newInstance(Metadata.class);

        File xml = new File("src/forum10699038/input.xml");
        Unmarshaller unmarshaller = jcXML.createUnmarshaller();
        Metadata metadata = (Metadata) unmarshaller.unmarshal(xml);

        // WRITE TO JSON
        Map<String, Object> properties = new HashMap<String, Object>(3);
        properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum10699038/oxm.xml");
        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
        JAXBContext jcJSON = JAXBContext.newInstance(new Class[] {Metadata.class}, properties);

        Marshaller marshaller = jcJSON.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(metadata, System.out);
    }

}

Output

{
   "artist" : [ {
      "id" : "4302e264-1cf0-4d1f-aca7-2a6f89e34b36",
      "type" : "Group",
      "score" : "100",
      "name" : "Farming Incident",
      "ipi" : [ "1001" ]
   } ]
}

MOXy and Jersey

You can easily use MOXy as your JSON provider in a JAXB-RS environment such as Jersey:


OTHER FILES

Below are versions of your files I created to make sure everything worked properly.

input.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0">
    <artist-list offset="0" count="1">
        <artist ext:score="100" type="Group"
            id="4302e264-1cf0-4d1f-aca7-2a6f89e34b36">
            <name>Farming Incident</name>
            <ipi-list>
                <ipi>1001</ipi>
            </ipi-list>
        </artist>
    </artist-list>
</metadata>

Metadata

package forum10699038;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class Metadata {

    @XmlElement(name="artist-list")
    ArtistList artistList;

}

ArtistList

package forum10699038;

import java.util.List;

public class ArtistList {

    private List<Artist> artist;

}

Artist

package forum10699038;

import javax.xml.bind.annotation.*;

@XmlType(propOrder={"name", "ipiList"})
public class Artist {

    @XmlAttribute
    private String id;

    @XmlAttribute
    private String type;

    @XmlAttribute(namespace="http://musicbrainz.org/ns/ext#-2.0")
    private String score;

    @XmlElement(name="ipi-list")
    private IPIList ipiList;

    private String name;

}

IPList

package forum10699038;

import java.util.List;

public class IPIList {

    private List<String> ipi;

}

package-info

@XmlSchema( 
    namespace = "http://musicbrainz.org/ns/mmd-2.0#", 
    elementFormDefault = XmlNsForm.QUALIFIED,
    xmlns={
        @XmlNs(prefix="", namespaceURI = "http://musicbrainz.org/ns/mmd-2.0#")
    }
) 
@XmlAccessorType(XmlAccessType.FIELD)
package forum10699038;

import javax.xml.bind.annotation.*;
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks for this very detailed reply, sorry I should have replied earlier but havent quite understood if it will do what I want. I already have an object model created with vanilla JAXB from an XSD, can I use this object model with Moxy to generate my desired Json or do I have to create another separate object Model with Moxy in order to build the Json ? – Paul Taylor Jul 10 '12 at 21:09
  • @PaulTaylor - You can use your object model with MOXy. I created one for the answer to demonstrate what it might look like. – bdoughan Jul 10 '12 at 21:31
  • Marked as correct, is it possible to specify javax.xml.bind.context.factory in code rather than a file, as I only want to use Moxy when outputting json not when outputting xml (because there is no problem there and dont wat anything to change) – Paul Taylor Jul 11 '12 at 07:40
  • @PaulTaylor - If you use the `MOXyJsonProvider` class then MOXy will be used for the JSON binding and another JAXB provider will be used for XML (see http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html). Since MOXy implements that JAXB (JSR-222) specification you can be confident using it for your XML as well. FYI WebLogic switched to MOXy as the default JAXB provider: http://blog.bdoughan.com/2011/12/eclipselink-moxy-is-jaxb-provider-in.html – bdoughan Jul 11 '12 at 13:55
  • I'm following your example so not using MoxyJsonProvider (Or JAX-RS), my Xml output is already in production so I'd really would rather not do anything that could change the xml output in any way, so isn't there a way to specify javax.xml.bind.context.factory.programmatically between the two. Also is Moxy available in Maven. – Paul Taylor Jul 11 '12 at 20:58
  • @PaulTaylor - Here is a link to another answer I gave that should help out: http://stackoverflow.com/a/11419366/383861 – bdoughan Jul 11 '12 at 21:15
  • Thankyou, I now have it working, but i notice a difference with jersey output http://stackoverflow.com/questions/11448966/how-do-i-get-json-generated-by-moxy-to-understand-when-model-are-numbers . Also have an additional Moxy requirement http://stackoverflow.com/questions/11449219/can-i-get-moxy-to-rename-an-element-when-generating-json – Paul Taylor Jul 12 '12 at 09:50
  • @PaulTaylor - I have just posted answers to your two follow up questions. – bdoughan Jul 12 '12 at 11:05
  • thx, have one more http://stackoverflow.com/questions/11450509/how-an-i-get-moxy-to-just-output-on-one-line-to-save-space – Paul Taylor Jul 12 '12 at 11:30
  • @PaulTaylor - I have posted an answer to that question as well. – bdoughan Jul 12 '12 at 15:22
  • It seems there is a problem with mapping static classes http://stackoverflow.com/questions/11467530/how-do-you-get-moxy-oxml-mapping-file-to-recognise-static-classes – Paul Taylor Jul 13 '12 at 16:32
  • @BlaiseDoughan: It is great you have good command over this technology. But I feel it like this jaxb thing is not straight forward and it could have been a little easier than the current thing. What is your opinion ? – Siva Tumma Oct 25 '13 at 03:48
1

The JSON being created by Jersey is an exact JSOON representation of that model provided on the site. The problem you are facing here is that the site is providing an awkward data model, not that the framework is not doing the right thing.

Why does this service return an object of type artist-list instead of returning a list of artists? Why does the service have an ipi-list object also? The real question you should be asking should be, how should this be modeled to work better with all technologies.

stevedbrown
  • 8,862
  • 8
  • 43
  • 58
  • I understand your general point but you must consider this model is generated from an xml schema, I can't see how within xml I can have multiple ipi objects without an ipilist object. i.e it should be possible to have a model that can generate sensible xml and json – Paul Taylor Jun 12 '12 at 21:04
1

I personally don't like annotations too much. Have a habit of generating JSON/XML in plain code. :)

For example with Jackson (Gson also similar):

mapper = new ObjectMapper();
JsonNode root = mapper.createObjectNode();

JsonNode artist = mapper.createObjectNode();
artist.put("score", "100");
root.put("artist-list", artist);

ArrayNode ipiList = mapper.createArrayNode();
ipi.add("1001");
artist.put("ipi-list", ipiList);

It may looks a lot of work on surface. But for me this is very clear way of mapping JSON to objects. Basically having toJson() method in entity classes is my usual practice. Here is an example: https://github.com/richardzcode/metrics/blob/master/src/main/java/com/rz/metrics/core/entities

It is just me though.

For JAXB I believe you need to annotate your entity like below:

@XmlElement(name = "ipi-list")
private List<Ipi> ipi;
Richard
  • 11
  • 2
  • 1
    Yeah it is alot of work, and means every time the object model changes I have to update the json part of the code. Id rather work with the model itself and let something else deal with converting to xml or json – Paul Taylor Jul 11 '12 at 07:43
1

I think the problem that you are seeing with the ipiList is due to an issue with single element arrays/lists in an underlying library that Jersey uses for mapping to JSON.

The following blog post (not mine) shows one way to configure Jersey, and the comments link through to alternative (possibly simpler) ways to make the JSON representation correct for arrays:

http://tugdualgrall.blogspot.co.uk/2011/09/jax-rs-jersey-and-single-element-arrays.html

Stephen Souness
  • 272
  • 1
  • 3
0

I managed to solve JSON array and primitive field "bug" in Jersey json library. Secret ingredient is previusly mentioned JSONConfiguration and ContextResolver magic. See my following post it has a full code example, customized ContextResolver and rest Application class might be somewhat fuzzy logic in first look.

How to serialize Java primitives using Jersey REST

  • json array for zero or single-element Java lists
  • primitive integer or boolean fields without quotation chars
Community
  • 1
  • 1
Whome
  • 10,181
  • 6
  • 53
  • 65