tom
tom

Reputation: 5001

XMLEncoder in java for serialization

Im just wondering how i use XMLEncoder to serialize ArrayList<foo> where foo is my own made class.

Do i have to do anything in particular, ie define my own xml structure first and then call toString on each value in my list and write it out?

Can anyone point me to a good tutorial? http://java.sun.com/products/jfc/tsc/articles/persistence4/ Thats what i have been looking at but it doesnt seem to mention what to do with non library classes.

Thanks

Upvotes: 6

Views: 30470

Answers (4)

Jigar Joshi
Jigar Joshi

Reputation: 240908

If you are looking for XML Serialization I would suggest you to go for XStream

Person joe = new Person("Joe", "Walnes");
joe.setPhone(new PhoneNumber(123, "1234-456"));
joe.setFax(new PhoneNumber(123, "9999-999"));

String xml = xstream.toXML(joe);

<person>
  <firstname>Joe</firstname>
  <lastname>Walnes</lastname>
  <phone>
    <code>123</code>
    <number>1234-456</number>
  </phone>
  <fax>
    <code>123</code>
    <number>9999-999</number>
  </fax>
</person>

Upvotes: 10

Todd K
Todd K

Reputation: 51

I'm not sure why so many examples on the internet are incomplete or don't work when it would only take a little attention for them to be much more useful. Here is a complete example that can be implemented in one file (PersonBean.java) and it works!

// This example creates two PersonBeans, creates an ArrayList, adds the beans to the
// list, serializes the ArrayList to an XML file.
// It then loads from the XML file into a new ArrayList
//
// Keywords: ArrayList, Serialize, XMLEncode, XMLDecode
// Note: Change the XML file while the 10 second Thread.sleep is waiting to see that
// the data is actually loaded from the file.

import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.beans.XMLEncoder;
import java.beans.XMLDecoder;
import java.util.ArrayList;

public class PersonBean {
  private String name;
  private int    age;

  public String getName() {
    return name; 
  }

  public int getAge() {  
    return age;
  }

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

  public void setAge(int age) { 
    this.age = age; 
  }

  @Override
  public String toString() {
    return String.format("[PersonBean: name='%s', age=%d]", name, age);
  }

  public static void main(String[] args) {
    PersonBean person1 = new PersonBean();

    person1.setName("Joe");
    person1.setAge(30);

    PersonBean person2 = new PersonBean();

    person2.setName("Jane");
    person2.setAge(25);

    ArrayList arrayList1 = new ArrayList();
    arrayList1.add(person1);
    arrayList1.add(person2);

    try {
      System.out.println("List 'arrayList1' = '" + arrayList1 + "'");
      FileOutputStream outputStream = new FileOutputStream("PersonBean.xml");
      XMLEncoder encoder = new XMLEncoder(outputStream);
      encoder.writeObject(arrayList1);
      encoder.close();
      Thread.sleep(10000);
    } catch (Exception ex) {
    } 

    try {
      FileInputStream inputStream = new FileInputStream("PersonBean.xml");
      XMLDecoder decoder = new XMLDecoder(inputStream);
      ArrayList<PersonBean> arrayList2 = (ArrayList<PersonBean>) decoder.readObject();
      decoder.close();
      System.out.println("List 'arrayList2' = '" + arrayList2 + "'");
    } catch (Exception ex) {
    } 
  }
}

Upvotes: 2

Johansensen
Johansensen

Reputation: 401

XMLEncoder can be used for any classes, including user-defined ones- the article you mention does detail how to do this, but it is not very well written so it can be a little difficult to understand.

If your user-defined class follows the JavaBeans spec, then you can just use encoder.writeObject() to serialize a List<Foo> instance. This is because the XML output is just a series of instructions for how to recreate the given instance at runtime. The default PersistenceDelegate knows how to serialize a List structure, but only has default behaviour for unknown classes. By default it attempts to recreate a given object instance by calling its nullary (no-argument) constructor, and then setting its properties one by one to the values of the given instance- something guaranteed to be possible if the class is a JavaBean.

If your class has some properties that are read-only, i.e. they are set by the constructor and cannot be changed after construction time, then you have to do (slightly) more to get it to work. You can create a custom instance of DefaultPersistenceDelegate which recognizes your class, and knows how to pass the appropriate data to its constructor- you simply pass it the names of the properties as a list, and it will do the rest:

PersistenceDelegate fooDelegate = new DefaultPersistenceDelegate(new String[] {"propertyName1", "propertyName2"});
encoder.setPersistenceDelegate(Foo.class, fooDelegate);

If your class has constructor arguments that do not directly map to properties with getter methods, and/or there are other complexities to restoring object state, you can generally work around these by extending PersistenceDelegate and implementing the necessary behaviour yourself. However, if your class is very complex to recreate at runtime and you want it to be serialized, you should seriously consider redesigning it to reduce its complexity- this will make the whole process much easier, and will vastly reduce the chance of errors, as well as making it easier to change and extend in future.

Upvotes: 4

vanje
vanje

Reputation: 10383

There is nothing special about serializing an ArrayList with XMLEncoder.

Here is an example:

There is a bean class TestBean:

public class TestBean {

  private String name;
  private int age;

  public TestBean() {
    this.name = "";
    this.age = 0;
  }

  public TestBean(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // Getter and setter ...

  @Override
  public String toString() {
    return String.format("[TestBean: name='%s', age=%d]", name, age);
  }
}

And a class Main which serialize an ArrayList<TestBean> and read it back again:

public class Main {
  private static final String FILENAME = "testbeanlist.xml";

  public static void main(String[] args) {
    try {
      // Create a list of TestBean objects ...
      final List<TestBean> list = new ArrayList<TestBean>();
      list.add(new TestBean("Henry", 42));
      list.add(new TestBean("Tom", 11));

      System.out.println("Writing list to file " + FILENAME + ": " + list);

      // ... and serialize it via XMLEncoder to file testbeanlist.xml
      final XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(
          new FileOutputStream(FILENAME)));
      encoder.writeObject(list);
      encoder.close();

      // Use XMLDecoder to read the same XML file in.
      final XMLDecoder decoder = new XMLDecoder(new FileInputStream(FILENAME));
      final List<TestBean> listFromFile = (List<TestBean>) decoder.readObject();
      decoder.close();

      System.out.println("Reading list: " + listFromFile);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
  }
}

And then the output is:

Writing list to file testbeanlist.xml: [[TestBean: name='Henry', age=42], [TestBean: name='Tom', age=11]]
Reading list: [[TestBean: name='Henry', age=42], [TestBean: name='Tom', age=11]]

The toString() method in TestBean is only for pretty printing. It doesn't influence the XML serialization.

Upvotes: 11

Related Questions