Chao Jiang
Chao Jiang

Reputation: 493

JAXB @XmlRootElement with the same name

I have a very long XML like :

<Author>
    <id></id>
    <name></name>
    <title></title>
    <address></address>
     ....
</Author>

I'm using JAXB to parser the XML before.

JAXBContext.newInstance(Author.class);

And my Author.java

@XmlRootElement(name = "Author")
public class Author {
     private String id;
     private String name;
     private String title;
     private String address;
     ...
}

It works well but I don't want to parser the whole XML to a big Java bean every time.

So, I want to using below way:

Create Commentator.java

@XmlRootElement(name = "Author")
public class Commentator {
     private String id;
     private String name;
     // setters, getters
}

Create Analyst.java

@XmlRootElement(name = "Author")
public class Analyst {
     private String title;
     private String address;
     // setters, getters
}

And I write below code to test.

JAXBContext context = JAXBContext.newInstance(Analyst.class, Commentator.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
String xml = "<Author> <id>1</id> <name>A</name> <title>B</title> <address>C</address></Author>";
Commentator obj = (Commentator) unmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes()));
System.out.println(obj);

It will print the correct reslult.

If I want to get the Analyst.

Analyst a = (Analyst) unmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes()));

I will get the Exception: java.lang.ClassCastException: com.xxx.Commentator cannot be cast to com.xxx.Analyst

I'm not sure this way is correct to parser. But I really need such a func.

Upvotes: 0

Views: 1275

Answers (2)

Chao Jiang
Chao Jiang

Reputation: 493

Just find a quick way to do this:

  1. Register the pojo.

JAXBContext context = JAXBContext.newInstance(Analyst.class, Commentator.class);

  1. Handle the input. I'm convert the str-xml to StreamSource.
String xml = "<Author> <id>1</id> <name>A</name> <title>B</title> <address>C</address></Author>";
StreamSource source = new StreamSource(new ByteArrayInputStream(xml.getBytes()));
  1. Create the Unmarshaller.

Unmarshaller unmarshaller = context.createUnmarshaller();

  1. (important) When you unmarshal the data, give the second paramter (which class you want to unmarshal)

JAXBElement<Analyst> unmarshal = unmarshaller.unmarshal(source, Analyst.class);

And then, get what you want:

Analyst analyst = unmarshal.getValue();

  1. If need another pojo.(Note that unmarshaller & source can't re-use in a method )

JAXBElement<Commentator> unmarshal2 = unmarshaller2.unmarshal(source2, Commentator.class);

And then:

Commentator com = unmarshal2.getValue();

No error report and result is correct.

Upvotes: 1

Thomas Fritsch
Thomas Fritsch

Reputation: 10127

In my opinion it is a somewhat clumsy design to have several Java classes mapped with the same @XmlRootElement. But nevertheless, you are still able to achieve what you want.

You need different JAXBContexts for Analyst and Commentator.
And because a JAXBContext is a big object and JAXBContext.newInstance(...) takes quite a long time to execute, it makes sense to save these JAXBContext instances in staticvariables and reuse these instead of creating new ones:

private static JAXBContext analystContext;
private static JAXBContext commentatorContext;

if (analystContext == null)
    analystContext = JAXBContext.newInstance(Analyst.class);
if (commentatorContext == null)
    commentatorContext = JAXBContext.newInstance(Commentator.class);

And consequently you also need different Unmarshallers created from them:

Unmarshaller analystUnmarshaller = analystContext.createUnmarshaller();
Unmarshaller commentatorUnmarshaller = commentatorContext.createUnmarshaller();

Then you are able to unmarshal the same XML content to different root classes:

String xml = "<Author> <id>1</id> <name>A</name> <title>B</title> <address>C</address></Author>";
Analyst analyst = (Analyst) analystUnmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes()));
Commentator commentator = (Commentator) commentatorUnmarshaller.unmarshal(new ByteArrayInputStream(xml.getBytes()));

Upvotes: 1

Related Questions