I want to serialize a object hierarchy including a list of objects which derive from the base class 'Thing'. This works fine, including deserialization - but XML-Simple insists in writing an attribute which specifies the actual used Java-class
when I create a xml file with the java code below, the content is like this:
<example1>
<things>
<fruit class="com.mumpitz.simplexmltest.Apple" id="17">
<sugar>212</sugar>
</fruit>
<fruit class="com.mumpitz.simplexmltest.Orange" id="25" weight="11.2"/>
</things>
</example1>
but this is not what I want. I'd like to have
<example1>
<things>
<apple id="17">
<sugar>212</sugar>
</apple>
<orange id="25" weight="11.2"/>
</things>
</example1>
'apple' and 'orange' elements without a class attribute, not 'fruit' with such an attribute. Is this possible?
(The second xml complies to a existing schema; adding extra attributes is not an option)
Here's the code:
package com.mumpitz.simplexmltest;
import java.io.File;
import java.util.ArrayList;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
class Fruit {
@Attribute(name = "id")
protected final int id;
Fruit(
@Attribute(name = "id")
int id) {
this.id = id;
}
int getObjectId() {
return id;
}
}
@Root
class Apple extends Fruit {
private final int sugar;
@Element(type = Fruit.class)
public Apple(
@Attribute(name = "id")
int id,
@Element(name = "sugar")
int sugar) {
super(id);
this.sugar = sugar;
}
@Element(name = "sugar")
public int getSugar() {
return this.sugar;
}
@Override
public String toString() {
return "id: " + id + ", sugar: " + sugar;
}
}
@Root
class Orange extends Fruit {
@Attribute
public double weight;
public Orange(
@Attribute(name = "id")
int id) {
super(id);
}
@Override
public String toString() {
return "id: " + id + ", weight: " + weight;
}
}
@Root
public class Example1 {
@ElementList
public ArrayList<Fruit> things = new ArrayList<Fruit>();
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("things:\n");
for (int i=0; i<things.size(); i++) {
sb.append(" " + things.get(i).toString() + "\n");
}
return sb.toString();
}
//////////////////////////////////
static Example1 createDummy() {
Example1 d = new Example1();
d.things.add(new Apple(17, 212));
Orange or = new Orange(25);
or.weight = 11.2;
d.things.add(or);
return d;
}
static String msg;
static Example1 res;
static public String getMessage() {
String m = msg;
msg = null;
return m;
}
static public boolean write(String path) {
Serializer serializer = new Persister();
Example1 example = Example1.createDummy();
File result = new File(path);
try {
serializer.write(example, result);
} catch (Exception e) {
e.printStackTrace();
msg = e.getMessage();
return false;
}
return true;
}
static public boolean read(String path) {
Serializer serializer = new Persister();
File source = new File(path);
try {
res = serializer.read(Example1.class, source);
} catch (Exception e) {
e.printStackTrace();
msg = e.getMessage();
return false;
}
return true;
}
public static Object getResult() {
return res;
}
}
some hours later I found the solution. You simply have to
Use the @ElementListUnion annotation
package com.mumpitz.simplexmltest;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.ElementListUnion;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
// the base class
@Element
class Thing {
static int count=0;
Thing() {
this.id = ++count;
}
@Attribute
protected int id;
public int getId() {
return id;
}
}
// first derived class
@Element
class Car extends Thing {
@Attribute
private String name;
Car(@Attribute(name="name") String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "ID: " + id + " Car: " + name;
}
}
// second derived class
@Element
class House extends Thing {
@Attribute
private int price;
House(@Attribute(name="price") int price) {
this.price = price;
}
public int getPrice() {
return this.price;
}
@Override
public String toString() {
return "ID: " + id + " House: " + price;
}
}
// a class with a list of base class instances
@Root(name="ListOfThings")
public class Example4 {
// specify the derived classes used in the list
@ElementListUnion({
@ElementList(entry="house", inline=true, type=House.class),
@ElementList(entry="car", inline=true, type=Car.class)
})
private ArrayList<Thing> list = new ArrayList<Thing>();
public void add(Thing t) {
list.add(t);
}
public List<Thing> getProperties() {
return list;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Example4 contains " + list.size() + " elements:\n");
for (Thing t : list) {
sb.append(" " + t.toString() + "\n");
}
return sb.toString();
}
//////////////////////////////////
// test code
//////////////////////////////////
static String msg;
static Example4 res;
static public String getMessage() {
String m = msg;
msg = null;
return m;
}
static private Example4 createDummy() {
Example4 d = new Example4();
d.add(new Car("Mercedes"));
d.add(new House(34000000));
d.add(new Car("VW"));
d.add(new House(230000));
return d;
}
//////////////////////////////////
// serialize / deserialize
//////////////////////////////////
static public boolean write(String path) {
Serializer serializer = new Persister();
File result = new File(path);
Example4 example = Example4.createDummy();
try {
serializer.write(example, result);
} catch (Exception e) {
e.printStackTrace();
msg = e.getMessage();
return false;
}
return true;
}
static public boolean read(String path) {
Serializer serializer = new Persister();
File source = new File(path);
try {
res = serializer.read(Example4.class, source);
} catch (Exception e) {
e.printStackTrace();
msg = e.getMessage();
return false;
}
return true;
}
public static Object getResult() {
return res;
}
}