Details
-
Type: Bug
-
Status: Closed
-
Priority: Major
-
Resolution: Not A Bug
-
Affects Version/s: 1.3.1
-
Fix Version/s: None
-
Component/s: Annotations
-
Labels:None
-
JDK version and platform:Sun jdk1.6.0_13 for Windows
Description
This is what I want to achieve:
<message success="true"> <data> <record> </record> <record> </record> <record> </record> </data> </message>
where top level container - message - has one boolean attribute - success. Under the message there is one container - data - which contains data records, which might contain whatever.
So I have a message class:
@XStreamAlias("message") public class MessageDto { public MessageDto () { this.setSuccess(true); } public MessageDto (Boolean success) { this.setSuccess(success); } private Boolean success; private RecordDto record; public void setRecord(RecordDto record) { this.record = record; } public void setSuccess(Boolean success) { this.success = success; } public RecordDto getRecord() { return this.record; } public Boolean getSuccess() { return success; } }
persons class:
public class PersonsDto implements RecordDto { public String getRecordName() { return "data"; } @XStreamImplicit(itemFieldName="person") private List <PersonDto> persons = new LinkedList <PersonDto> (); public void addPerson (PersonDto person) { persons.add(person); } }
as seen, a list which contains persons is annotated whith @XStreamImplicit(itemFieldName="person") annotation.
A single person is:
@SuppressWarnings("unused") public class PersonDto { public PersonDto () { } public PersonDto (String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } private String firstName; private String lastName; public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } }
Now, finally, converter:
public class MessageConverter implements Converter { @SuppressWarnings("unchecked") public boolean canConvert(Class type) { return (MessageDto.class == type); } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { MessageDto message = (MessageDto) source; writer.addAttribute("success", message.getSuccess().toString()); writer.startNode(message.getRecord().getRecordName()); context.convertAnother(message.getRecord()); writer.endNode(); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return null; } }
now the test:
public class ConverterTest { public static void main(String[] args) throws IOException { XStream xStream = new XStream(); xStream.registerConverter(new MessageConverter()); xStream.autodetectAnnotations(true); PersonsDto persons = new PersonsDto (); persons.addPerson(new PersonDto("Charles", "Barkley")); persons.addPerson(new PersonDto("Michael", "Jordan")); persons.addPerson(new PersonDto("Kobe", "Bryant")); MessageDto message = new MessageDto () ; message.setRecord(persons); System.out.println(xStream.toXML(message)); System.out.println(xStream.toXML(message)); } }
now first call to xStream.toXML method gives me this:
<message success="true"> <data> <persons class="linked-list"> <com.xstr.dto.PersonDto> <firstName>Charles</firstName> <lastName>Barkley</lastName> </com.xstr.dto.PersonDto> <com.xstr.dto.PersonDto> <firstName>Michael</firstName> <lastName>Jordan</lastName> </com.xstr.dto.PersonDto> <com.xstr.dto.PersonDto> <firstName>Kobe</firstName> <lastName>Bryant</lastName> </com.xstr.dto.PersonDto> </persons> </data> </message>
as seen, the @XStreamImplicit annotation seems to be completely ingored. What is more surprising, the second call, just below the first one, gives correct result:
<message success="true"> <data> <person> <firstName>Charles</firstName> <lastName>Barkley</lastName> </person> <person> <firstName>Michael</firstName> <lastName>Jordan</lastName> </person> <person> <firstName>Kobe</firstName> <lastName>Bryant</lastName> </person> </data> </message>
A workaround for this is to register an implicit collection with an XStream instance programatically
XStream xStream = new XStream(new DomDriver()); xStream.registerConverter(new MessageConverter()); xStream.autodetectAnnotations(true); xStream.addImplicitCollection(PersonsDto.class, "persons", "person", PersonDto.class);
with this change, the xml is correct from the first time. However, in my case, it is not that simple as it seems, since I am using Spring oxm support and have to configure it wihthin an applicationContext.xml file, but it is doable.
Hi Wiktor,
this is not a bug in XStream. The annotation auto-detection mode relies on the fact that the converter implementations will look up the type names of the objects in the handled object using a mapper. Your converter implementation has no need to use this, because you obtain the tag name of the child element using the record's name and since you are not interested in deserializing, you also do not have the need to write the type of your RecordDto (i.e. the PersonDto) into the XML stream. However, this will also skip the annotation detection in advance and when the PersonDto is handled (and its annotations are processed) it is already too late (at least for the first run). In your case you have to preprocess the PersonDto's annootation manually.