views:

118

answers:

4

I'm looking for a way to serialize Java objects into XML for use by a RESTful web service. I don't have an XSD.

I have looked at the following:

  1. JAXB - fairly heavy weight with annotations required on classes and also an ObjectFactory class and/or a jaxb.index file

  2. Simple - requires annotations but no other config classes/files. Unfortunately it can't serialize Sets.

  3. XStream - no annotations etc. required, but doesn't support generics

Does anyone else have any suggestions?

+4  A: 
import java.beans.XMLEncoder;
import java.beans.XMLDecoder;
import java.io.*;

public class XMLSerializer {
    public static void write(Object f, String filename) throws Exception{
        XMLEncoder encoder =
           new XMLEncoder(
              new BufferedOutputStream(
                new FileOutputStream(filename)));
        encoder.writeObject(f);
        encoder.close();
    }

    public static Object read(String filename) throws Exception {
        XMLDecoder decoder =
            new XMLDecoder(new BufferedInputStream(
                new FileInputStream(filename)));
        Object o = (Object)decoder.readObject();
        decoder.close();
        return o;
    }
}
Romain Hippeau
Thanks for the suggestion. I tried it out and the XML contains stuff that describes the class (e.g. <object id="obj0" class="my.package.Class"> which I don't want to be part of the response sent back in the web service response. This makes sense for the intended purpose of the class. However, it's not what I need.
sdoca
A: 

My vote would be for XStream. The lack of generics support is a small price to pay for the amount of flexibility it offers. You can also easily implement generics by serializing the generic class type at serialization time, or build this into your domain objects. E.g.

   class Customer
   {
      List<Order> orders;

      public List<Order> getOrders()
      {
         return orders;
      }
   }

In the stream, the element type denotes the type of each object. When the type is an abstract type or interface, the element listed with the implementing class, unless that has been specified as the default for that interface type. (E.g. ArrayList as the default for instances of static type List.)

Generics are "rose coloured glasses" in java - they don't really change what you are seeing, just how you see it. The objects would be sent over the wire exactly the same if they were sent with generics support or not.

mdma
My vote would be for XStream as well if it supported generics. I don't understand what you mean by "serializing the generic class type at serialization type". Also, your example doesn't look significantly different from mine (excpept that my collection type is a Set, not a List). How is this building the serialization type into the object?
sdoca
@sdoca - sorry that's a typo - I meant "serialization time". The point is that you don't need generics in the stream, since the actual types are stored explicitly, or implied - the same objects are serialized regardless of whether the collection has generics or not. Can you give an example of what XStream doesn't do for you and I'll see if I can help you with that.
mdma
I re-coded my test class to use XStream to confirm the issue I had was that the Collection elements were not included in the XML. I did some research and found a mailing list thread that stated it wasn't supported. Perhaps that was old?Is it normal for the XML to look include a "reference" to the parent object in the collection element? E.g.<ObjectA> <objectBs> <ObjectB> <objectA reference="../../.."/> </ObjectB> </objectBs></ObjectA>
sdoca
If the objectB includes a reference to objectA, and that object has already been serialized, then yes, you will see that same reference in the XML. If you want this reference to be implied, then you will have to build a custom marshaller. Unless you're planning on writing/reading the XML by hand, I wouldn't worry too much about the precise format. (Having said that, getting rid of the parent reference was one of the first things I tried doing. I gave up in the end - the effort wasn't worth it. ) If your web clients are not java, you may want to think twice before not starting with a schema.
mdma
For my purposes, I really don't want the references in my XML so I ended up hand crafting the XML. However, this truly is the easiest way I found to serialize an object into XML, so marking this as the answer.
sdoca
A: 

JAXB is pretty painless, and comes with Java 6, and it has a low footprint.

You get this for the price of one annotation.

Order.java

package xxx;


import java.util.Date;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Order {

    int custId;
    Date orderDate;
    String description;
    List<Item> items;

    public void setCustId(int custId) {
        this.custId = custId;
    }

    public void setOrderDate(Date orderDate) {
        this.orderDate = orderDate;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setItems(List<Item> items) {
        this.items = items;
    }

    public int getCustId() {
        return custId;
    }

    public Date getOrderDate() {
        return orderDate;
    }

    public String getDescription() {
        return description;
    }

    public List<Item> getItems() {
        return items;
    }

    public String toString() {
        return "Order: " + custId + " - " + orderDate + " - " + description + " - " + items;
    }
}

Item.java:

package xxx;

public class Item {

    String name;
    private int qty;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getQty() {
        return qty;
    }

    public void setQty(int qty) {
        this.qty = qty;
    }

    public String toString() {
        return "Item:" + name + " - " + qty;
    }
}

Test.java:

package xxx;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Test {

    public static void main(String args[]) throws Exception {
    Order o = new Order();
        o.setCustId(123);
        o.setDescription("New order");
        o.setOrderDate(new Date());

        List<Item> items = new ArrayList<Item>();

        Item i = new Item();
        i.setName("Duck Soup");
        i.setQty(10);
        items.add(i);

        i = new Item();
        i.setName("Carrots");
        i.setQty(4);

        items.add(i);

        o.setItems(items);


        //Write it
        JAXBContext ctx = JAXBContext.newInstance(Order.class);

        Marshaller m = ctx.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter sw = new StringWriter();
        m.marshal(o, sw);
        sw.close();
        System.out.println(sw.toString());

        // Read it back
        JAXBContext readCtx = JAXBContext.newInstance(Order.class);
        Unmarshaller um = readCtx.createUnmarshaller();

        Order newOrder = (Order) um.unmarshal(new StringReader(sw.toString()));
        System.out.println(newOrder);
    }
}

Results:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<order>
    <custId>123</custId>
    <description>New order</description>
    <items>
        <name>Duck Soup</name>
        <qty>10</qty>
    </items>
    <items>
        <name>Carrots</name>
        <qty>4</qty>
    </items>
    <orderDate>2010-06-16T18:12:06.870-07:00</orderDate>
</order>

Order: 123 - Wed Jun 16 18:12:06 PDT 2010 - New order - [Item:Duck Soup - 10, Item:Carrots - 4]
Will Hartung
Your example doesn't show the need for either an ObjectFactory class and/or jaxb.index file which, correct me if I'm wrong, is mandatory. So you not only have to annotate your classes you have extra config to do after that.
sdoca
That's right, your wrong. If an ObjectFatory was required, then the example wouldn't work, would it? But it does work, which you would know if you actually tried it and trusted that I would take the time to post an accurate example and have some idea what I'm talking about rather than relying on your preconceptions. I've never used either of those things in my projects. This shows what you can get using defaults out of the box. For advanced cases, perhaps they're necessary, but I've never needed them for my own objects (vs a published schema) in my projects. I do use more annotations, however.
Will Hartung
When I tried to annotate only one of my classes as you did, I got an error because my objects have references to one another and would cause an infinite loop.When I annotate each class/attribute, I get the error "Exception in thread 'main' javax.xml.bind.JAXBException: 'mypackage' doesnt contain ObjectFactory.class or jaxb.index". Sorry I offended you by asking a question, but my experience with JAXB differed from yours.
sdoca
A: 

You can try JLibs XMLDocument. It uses SAX to create XML and thus light-weight and have complete control.

Santhosh Kumar T