5

I am trying to parse JSON object using Jettison. This the code I'm using

String s ="{\"appUsage\":[{\"appName\":\"ANDROID\",\"totalUsers\":\"0\"},{\"appName\":\"IOS\",\"totalUsers\":\"4\"}]}";

JSONObject obj = new JSONObject(s);

ArrayList<MiAppUsage> l1 =  (ArrayList<MiAppUsage>) jsonParser(ArrayList.class, obj);

public static Object jsonParser(Class c, JSONObject obj)
            throws JSONException, XMLStreamException, JAXBException {
        JAXBContext jc = JAXBContext.newInstance(c);
        Configuration config = new Configuration();
        MappedNamespaceConvention con = new MappedNamespaceConvention(config);
        XMLStreamReader xmlStreamReader = new MappedXMLStreamReader(obj, con);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        ArrayList<MiAppUsage>customer = (ArrayList<MiAppUsage>) unmarshaller.unmarshal(xmlStreamReader);
        return customer;
    }

I'm getting this error

Exception in thread "main" javax.xml.bind.UnmarshalException - with linked exception: [javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"appUsage"). Expected elements are (none)] at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source) at com.json.UnmarshalDemo.jsonParser(UnmarshalDemo.java:56) at com.json.UnmarshalDemo.main(UnmarshalDemo.java:33) Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"appUsage"). Expected elements are (none) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(Unknown Source) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(Unknown Source) ... 4 more Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"appUsage"). Expected elements are (none) ... 14 more

How to resolve this problem

prem
  • 163
  • 2
  • 3
  • 5

3 Answers3

5

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

If you are ultimately looking for a way to interact with JSON using a JAXB (JSR-222) implementation, then the following is how it can be done using MOXy. Jettison is an interesting library but there are some issue you will encounter using it:

Demo

Only the standard Java SE APIs are used. There are two MOXy specific properties that need to be set on the Unmarshaller: "eclipselink.media-type" to specify "application/json", and "eclipselink.json.include-root" to indicate that there is no root node.

package forum9924567;

import java.io.StringReader;
import java.util.ArrayList;

import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    private static final String s ="{\"appUsage\":[{\"appName\":\"ANDROID\",\"totalUsers\":\"0\"},{\"appName\":\"IOS\",\"totalUsers\":\"4\"}]}";

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty("eclipselink.media-type", "application/json");
        unmarshaller.setProperty("eclipselink.json.include-root", false);
        StreamSource json = new StreamSource(new StringReader(s));
        Root root = unmarshaller.unmarshal(json, Root.class).getValue();

        ArrayList<MiAppUsage> customer = root.appUsage;
        for(MiAppUsage miAppUsage : customer) {
            System.out.print(miAppUsage.appName);
            System.out.print(' ');
            System.out.println(miAppUsage.totalUsers);
        }
    }

}

Root

I had to introduce this class to meet your use case. We could eliminate this class if your JSON looked like: [{"appName":"ANDROID","totalUsers":"0"},{"appName":"IOS","totalUsers":"4"}].

package forum9924567;

import java.util.ArrayList;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    ArrayList<MiAppUsage> appUsage;

}

MiAppUsage

package forum9924567;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class MiAppUsage {

    String appName;
    int totalUsers;

}

jaxb.properties

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

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

Output

The following is the output from running the demo code:

ANDROID 0
IOS 4

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400
1

I think this cannot be done with Jettison. Seems like the JSONObject does not like the root element having an array as its value. If it's an option, you may change your input into a JSON array, which can be parsed with Jettison:

public static void main(String[] args) throws Exception {
  String s ="[{\"appUsage\":{\"appName\":\"ANDROID\",\"totalUsers\":\"0\"}},{\"appUsage\":{\"appName\":\"IOS\",\"totalUsers\":\"4\"}}]";

  JSONArray arr = new JSONArray(s);

  List<MiAppUsage> list = unmarshal(MiAppUsage.class, arr);
  for(MiAppUsage miAppUsage : list) {
    System.out.println(miAppUsage.appName + ": " + miAppUsage.totalUsers);
  }
}

public static <T> List<T> unmarshal(Class<T> cls, JSONArray arr) throws JSONException, XMLStreamException, JAXBException {
  List<T> list = new ArrayList<T>();
  for (int i = 0; i < arr.length(); i++) {
    list.add(unmarshal(cls, arr.getJSONObject(i)));
  }
  return list;
}

public static <T> T unmarshal(Class<T> cls, JSONObject obj) throws JSONException, XMLStreamException, JAXBException {
  JAXBContext jc = JAXBContext.newInstance(cls);
  Configuration config = new Configuration();
  MappedNamespaceConvention con = new MappedNamespaceConvention(config);
  XMLStreamReader xmlStreamReader = new MappedXMLStreamReader(obj, con);

  Unmarshaller unmarshaller = jc.createUnmarshaller();
  return cls.cast(unmarshaller.unmarshal(xmlStreamReader));
}

This prints:

ANDROID: 0
IOS: 4

The JAXB-annotated model class is like this:

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "appUsage")
@XmlAccessorType(XmlAccessType.FIELD)
public class MiAppUsage {
  String appName;
  int totalUsers;
}

As an alternative to Jettison, your original JSON input can be easily parsed with StAXON's JsonXMLMapper, which works with any JAXB implementation:

String s ="{\"appUsage\":[{\"appName\":\"ANDROID\",\"totalUsers\":\"0\"},{\"appName\":\"IOS\",\"totalUsers\":\"4\"}]}";

JsonXMLMapper<MiAppUsage> mapper = new JsonXMLMapper<MiAppUsage>(MiAppUsage.class);
List<MiAppUsage> list = mapper.readArray(new StringReader(s));

for(MiAppUsage miAppUsage : list) {
  System.out.println(miAppUsage.appName + ": " + miAppUsage.totalUsers);
}

For more information on StAXON's JAXB support, refer to the Using JAXB wiki page.

chris
  • 3,573
  • 22
  • 20
  • Coming back to OP's original question, how do we solve this problem using Jettison? – Jyotirup Mar 30 '12 at 16:39
  • 1
    Jettison seems to have a problem here. I edited my answer and added a solution using Jettison with a slightly modified input. – chris Mar 30 '12 at 20:06
0

Annotate your class MiAppUsage with @XmlRootElement(name = "appUsage", namespace="")

eg:- @XmlRootElement(name = "appUsage", namespace="") public class MiAppUsage {

}

Varun
  • 89
  • 6