The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi

Kohsuke Kawaguchi's Blog

Using JAXB 2.0 to persist your own classes to XML

Posted by kohsuke on February 17, 2005 at 05:15 PM | Comments (11)

One of the major enhancements in the JAXB 2.0 is an ability to bind your own hand-written classes --- often called as POJOs --- to XML. And it's very easy.

For example, consider the following classes:
public class TodoList {
  private final List<Item> items = new ArrayList<Item>();
  
  ... methods ...
}

public class Item {
  private String title;
  private Kind type;
  private Calendar timestamp;
  private String description;
  
  ... methods ...
}

public enum Kind {
  PERSONAL, WORK
}
To bind these classes to XML, you need to identify what the root object to be persisted, and you need to give it an XML tag name. This is done by using the XmlRootElement annotation.
@XmlRootElement
public class TodoList {
  ...
}
Then you just compile these classes normally. Then you create a JAXBContext object like this:
JAXBContext context = JAXBContext.newInstance(TodoList.class);
This will cause JAXB to scan the TodoList class and prepare it for binding. Because this class refers to the Item class and the Kind class, those classes will be also automatically scanned and prepared.

To write out a TodoList object, you do:
TodoList todo = ...;
context.createMarshaller().marshal(todo,System.out);
and you'll get something like:
<todoList>
  <items>
    <title>Dinner with the boss</title>
    <type>WORK</type>
    <timestamp>2005-02-18T18:00:00Z</timestamp>
    <description>At Chewy's</description>
  </items>
  <items>
    <title>Our 5th anniversary</title>
    <type>PERSONAL</type>
    <timestamp>2005-02-20T18:00:00Z</timestamp>
    <description>Don't forget to buy flowers!</description>
  </items>
</todoList>
You can then read this back to Java object like this:
TodoList todo = (TodoList)context.createUnmarshaller().unmarshal(System.in);
Unlike other similar technologies, JAXB allows you to control the XML representation by using annotations. For example, I don't like the element name "items", so let's change it to "todo". You can do that adding the following annotation:
public class TodoList {
  @XmlElement(name="todo")
  private final List items = new ArrayList;
  ...
}
While we are at it, why don't we change the "type" to be an attribute. To do that you use another annotation like this:
public class Item {
  @XmlAttribute
  private Kind type;
  ...
}
Now the same code would produce the following XML:
<todoList>
  <todo type="WORK">
    <title>Dinner with the boss</title>
    <timestamp>2005-02-18T18:00:00Z</timestamp>
    <description>At Chewy's</description>
  </todo>
  <todo type="PERSONAL">
    <title>Our 5th anniversary</title>
    <timestamp>2005-02-20T18:00:00Z</timestamp>
    <description>Don't forget to buy flowers!</description>
  </todo>
</todoList>
Not only can JAXB able to persist data to XML, it can also generate a schema from this (although that code didn't make it into the currently available drop).

As usual, this is still a technology in development, so things could change before it becomes final. You can try this today by using the technology preview release of the JAXB RI 2.0. See here for how to download it.

If you have any thoughts, please let us hear it at the forum.

Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • There is a project "backport175" from codehaus which
    backports the jdk1.5 annotations to jdk1.3/1.4.
    Could we now have jaxb 2.0 on jdk1.4?
    see:
    http://www.theserverside.com/://www.theserverside.com/news/thread.tss?thread_id=31882

    Posted by: keeskuip on February 19, 2005 at 11:12 AM

  • Cool!

    What are the demands on the objects to be marshalled? get/set pattern? How about transient (if not get/set)? Public/private constructors? And such.

    It looks very promising!

    Cheers,
    Mikael Grev

    Posted by: mgrev on February 19, 2005 at 10:40 PM

  • The exact details of "what object qualifies for JAXB" are stil under discussion. And they are likely to change some more.

    And yes, in terms of the capability, it does allow you to annotate get/set methods and/or annotate fields, regardless of access modifiers. The discussion is mostly around which of those should be picked up by default. I think the side that argues "getter/setter should be the default" is winning, and I'm on the losing side. The technology preview version of the JAXB RI actually looks for fields by default if there's no annotation.

    Yes, we have @XmlTransient.

    Currently the JAXB RI TP requires a no-argument constructor (I think it didn't check for the access modifiers but I'm not sure.) At one point internally we talked about not requiring a no-arg constructor at all, but our current thinking is that it's probably difficult to specify so in the spec level, because this requires a non-standard dependency to JRE.

    We are all very interested in knowing how people feel about these issues. Please send in your feelings to the forum.

    Posted by: kohsuke on February 20, 2005 at 08:46 AM

  • About backport175, see this thread in the forum.

    Posted by: kohsuke on February 20, 2005 at 08:47 AM

  • kohsuke,

    Ok here's my $1 on some of it. My experience is that I have written a lot of delegates for the JavaBeans XML Encoder/Decoder, in our MiG Calendar prodict.

    You should support non-empty constructors in some way, mainly because it's needed for true immutable objects. Otherwise those objects will have to be opened for builder like code. The only other option is that you have some magic which means that you can build an object without setXXX methods and a no-arg constructor, but that seams a bit hackish to me. Maybe you can annotate the class with @constructor("propName1", "propName3", "propName3", ...) to use that construcot instead of a no-arg one. Btw, maybe it's better to annotate the constructor instead and only allow one constructor to be annotated for persistence, that way the annotation comes closer to the code which is good to protect agaist accidental constructor changes that would break it.

    I am a big fan of true immutable objects and think it would be a misstake to build a persistance fw that made them impossible to make, even if the constructor can be private.

    Defaults, can't you have class annotations that specify the default behaviour? @defaultPersistGetSet("true"), @defaultPersistFields("true") (but with much better names) just so that you don't HAVE to annotate every field/method. Field and method annotations can be used to override the default behaviour.

    Cheers,
    Mikael

    Posted by: mgrev on February 20, 2005 at 01:48 PM

  • I've played around a lot with the early access release of JAXB 2.0 and currently this is how it plays out for immutable objects.

    - You need to have a no-arg constructor, but it can be package or protected. It doesn't seem to be possible to have a private constructor because IllegalAccessException gets thrown.

    - You can have private variables and no setter methods.

    - You cannot have private final variables as far as I can tell. If JAXB sees a final variable and no setter method, it fails.

    As to the discussion about JRE support, unless I'm mistaken Java serialization specification does not require a default constructor, can leverage final variables and pretty much supports all manner of objects including the uber immutable variety. The specification calls for accessibility to one default constructor in the first non-serializable super-class. That isn't a bad requirement and many objects get it for free from Object. So, I would think that JAXB 2.0 could copy/paste from that specification and leverage much of the same code.

    I've gotten around all of these things by having package constructors and no setters. I think that private final variables and no default constructor is definitely needed because without immutability, JAXB is rendered dangerous and cumbersome in many environments.

    Posted by: voidmain on February 21, 2005 at 05:49 PM

  • Thanks for the info voidmain,

    It's too bad you can't use true immutable objects, I create them all the time. You basically KNOW that they will be correct in all situations. you don't have to spend time investigating them, especially if you are debugging code that many have been touching.

    I hope they will change this or at least I can not use it.

    Cheers,
    Mikael

    Btw, you have to use to make new lines...

    Posted by: mgrev on February 21, 2005 at 09:41 PM

  • Btw, here is a little article I written on the immuatable subject

    Posted by: mgrev on February 21, 2005 at 09:44 PM

  • Thanks for a lot of comments. Looks like many people have strong feeling about immutable objects. To be honest, me too. I use them all the time. I'm also a big fan of using final fields.
    I'll write another blog on this issue and explain why some of the things mentioned here is tricky.

    Posted by: kohsuke on February 22, 2005 at 06:26 AM

  • Great Article!

    I hope this is an ok place to ask this question.

    I have hibernate pojos which I would like to serialize to xml with jaxb however Im currently using JWSDP 1.6 which I beleive has jaxb 1.0.5.

    Problem Im having is that my jaxb generated classes do not have default no arg constructors, So I cant use them with hibernate.

    I'm aware of hyperjaxb and also that it looks like from this blog that this is doable in jaxb 2.0 however is there a way to tell xjc to generate a default no arg constructor with the RI of jaxb that comes with JWSDP?

    Thanks in advance,

    R
    S

    Posted by: robsinner on December 07, 2005 at 09:50 PM

  • Sorry, I have exception :( , I don't understand why?
    javax.xml.bind.MarshalException
    - with linked exception:
    [com.sun.istack.SAXException2: unable to marshal type "test.MyObject" as an element because it is missing an @XmlRootElement annotation]
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:295)br/

    Source code:
    public class TestJAXB {

    public static void main(String[] in){
    new TestJAXB();
    }

    public TestJAXB() {
    try {
    MyObject myObject = getMyObject();
    JAXBContext context =
    JAXBContext.newInstance(MyObject.class);
    context.createMarshaller().marshal(myObject, System.out);
    } catch (Exception ex) {
    ex.printStackTrace();
    }
    }

    private MyObject getMyObject() {
    MyObject myObject = new MyObject();
    myObject.setPk("this PK");
    return myObject;
    }

    }

    package test;
    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.*;

    @XmlRootElement
    public class MyObject {
    private String pk;
    public MyObject() { }

    public void setPk(String pk) {
    this.pk = pk;
    }
    public String getPk() {
    return pk;
    }
    }

    Posted by: mors78 on June 21, 2006 at 06:55 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds