[英]Custom XStream converter creates unexpeted output for referenced objects
For ObjectProperty
I have created a custom converter for XStream
: 对于ObjectProperty
我为XStream
创建了一个自定义转换器:
public class ObjectPropertyConverter extends AbstractPropertyConverter<Object> implements Converter {
public ObjectPropertyConverter(Mapper mapper) {
super(ObjectProperty.class, mapper);
}
@Override
protected WritableValue<Object> createProperty() {
return new SimpleObjectProperty();
}
@Override
protected Class readType(HierarchicalStreamReader reader) {
return mapper.realClass(reader.getAttribute("propertyClass"));
}
@Override
protected void writeValue(HierarchicalStreamWriter writer, MarshallingContext context, Object value) {
final Class clazz = value.getClass();
final String propertyClass = mapper.serializedClass(clazz);
writer.addAttribute("propertyClass", propertyClass);
context.convertAnother(value);
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
WritableValue<Object> property = createProperty();
if (reader.getAttribute("propertyClass") != null) {
final Object value = context.convertAnother(null, readType(reader));
property.setValue(value);
}
return property;
}
}
public abstract class AbstractPropertyConverter<T> implements Converter {
private final Class clazz;
protected final Mapper mapper;
public AbstractPropertyConverter(Class clazz, Mapper mapper) {
this.clazz = clazz;
this.mapper = mapper;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
if (source != null) {
T value = ((ObservableValue<T>) source).getValue();
if (value != null) {
writeValue(writer, context, value);
}
}
}
protected void writeValue(HierarchicalStreamWriter writer, MarshallingContext context, T value) {
context.convertAnother(value);
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
WritableValue<T> property = createProperty();
final T value = (T) context.convertAnother(null, readType(reader));
property.setValue(value);
return property;
}
protected abstract WritableValue<T> createProperty();
protected abstract Class<? extends T> readType(HierarchicalStreamReader reader);
public boolean canConvert(Class type) {
return clazz.isAssignableFrom(type);
}
}
In most cases this does the job just fine. 在大多数情况下,这做得很好。 However I have run into an issue when there is a list of items, that link to each other through an ObjectProperty
. 但是,当存在通过ObjectProperty
彼此链接的项目列表时,我遇到了一个问题。 As long as the linked item is in the list before it is linked to it works and a reference is used. 只要链接的项目在链接到列表之前就在列表中就可以使用,并且使用引用。 However the other way around it does not work. 但是,解决该问题的另一种方法无效。 Here is the sample application to create said situation: 这是创建上述情况的示例应用程序:
public class ObjectPropertyReferenceSerializer {
public static void main(String[] args) {
Item item1 = new Item("item1");
Item item2 = new Item("item2");
Item item3 = new Item("item3");
item2.setParent(item1);
item1.setParent(item3);
List<Item> list = asList(item1, item2, item3);
XStream xStream = getXstreamObject();
// Save to file
String filename = "xStreamCrayersWithReference.xml";
try {
xStream.toXML(list, Files.newOutputStream(Paths.get(filename), StandardOpenOption.CREATE));
} catch (IOException e) {
e.printStackTrace();
}
}
private static XStream getXstreamObject() {
XStream xstream = new XStream(); // DomDriver and StaxDriver instances also can be used with constructor
xstream.setMode(XStream.ID_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.registerConverter(new ObjectPropertyConverter(xstream.getMapper()));
return xstream;
}
}
And this output is created: 并创建以下输出:
<java.util.Arrays_-ArrayList id="1">
<a class="ch.sahits.game.openpatrician.app.model.Item-array" id="2">
<ch.sahits.game.openpatrician.app.model.Item id="3">
<uuid>3e0d8b93-b283-4244-997f-d78e7f6e1954</uuid>
<name>item1</name>
<parent class="javafx.beans.property.SimpleObjectProperty" id="4" propertyClass="ch.sahits.game.openpatrician.app.model.Item">
<uuid>fb2ca551-8a0d-450c-966f-4261fdde1d7d</uuid>
<name>item3</name>
<parent class="javafx.beans.property.SimpleObjectProperty" id="6"/>
</parent>
</ch.sahits.game.openpatrician.app.model.Item>
<ch.sahits.game.openpatrician.app.model.Item id="7">
<uuid>524e325c-188a-47d5-914d-53581461e6c9</uuid>
<name>item2</name>
<parent class="javafx.beans.property.SimpleObjectProperty" id="8" propertyClass="ch.sahits.game.openpatrician.app.model.Item" reference="3"/>
</ch.sahits.game.openpatrician.app.model.Item>
<ch.sahits.game.openpatrician.app.model.Item id="9">
<uuid>fb2ca551-8a0d-450c-966f-4261fdde1d7d</uuid>
<name>item3</name>
<parent class="javafx.beans.property.SimpleObjectProperty" reference="6"/>
</ch.sahits.game.openpatrician.app.model.Item>
</a>
</java.util.Arrays_-ArrayList>
The items with ids 4 and 9 are the same. ID为4和9的项目相同。 Only when it is added as the value of an ObjectProperty
it is inlined, meaning the outer most element () is missing. 仅当将其添加为ObjectProperty
的值时才进行内联,这意味着最外面的元素()丢失了。
How can I get the converter to produce output, that can be properly referenced? 我怎样才能使转换器产生可以正确引用的输出?
Replacing the converter with a more straight forward approach seems to work: 用更直接的方法替换转换器似乎可行:
public class ObjectPropertyConverterV2 implements Converter {
private Mapper mapper;
public ObjectPropertyConverterV2(Mapper mapper) {
this.mapper = mapper;
}
@Override
public void marshal(Object o, HierarchicalStreamWriter hierarchicalStreamWriter, MarshallingContext marshallingContext) {
ObjectProperty objProperty = (ObjectProperty) o;
if (objProperty.get() != null) {
hierarchicalStreamWriter.startNode(mapper.serializedClass(objProperty.get().getClass()));
marshallingContext.convertAnother(objProperty.get());
hierarchicalStreamWriter.endNode();
}
}
@Override
public Object unmarshal(HierarchicalStreamReader hierarchicalStreamReader, UnmarshallingContext unmarshallingContext) {
String type = hierarchicalStreamReader.getAttribute("class");
ObjectProperty objProperty;
try {
objProperty = (ObjectProperty) getClass().getClassLoader().loadClass(type).newInstance();
} catch (ClassNotFoundException|InstantiationException|IllegalAccessException e) {
log.warn("Failed to create instance of type " + type, e);
objProperty = new SimpleObjectProperty();
}
if (hierarchicalStreamReader.hasMoreChildren()) {
hierarchicalStreamReader.moveDown();
Object value = unmarshallingContext.convertAnother(null, mapper.realClass(hierarchicalStreamReader.getNodeName()));
hierarchicalStreamReader.moveUp();
objProperty.setValue(value);
}
return objProperty;
}
@Override
public boolean canConvert(Class aClass) {
return ObjectProperty.class.isAssignableFrom(aClass);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.