0

I have a doubt whether it is possible to ignore the xml root element on a conversion form Object to XML with XStream, or if is there any way to replace the root element with other one, I mean:

I have a function to parse custom runtime-created objects to XML, something like:

public static String entityToXML(GenericResponseObject entity) {
    XStream xstream = new XStream(new StaxDriver());

    xstream.autodetectAnnotations(true);
    xstream.registerConverter(new GenericResponseAttributeConverter());
    String xml = xstream.toXML(entity);

    return xml;
}

For this, I have made a mess:

I have the GenericResponseObject and the GenericResponseAttribute clases, the idea is to have an object with as many custom attributes as needed on runtime:

@XStreamAlias("objectResult")
public class GenericResponseObject {

    @XStreamAlias("attributes")
    @XStreamImplicit
    private ArrayList<GenericResponseAttribute> attributes;

    public GenericResponseObject() {
        this.attributes = new ArrayList();
    }

    public void addAttribute(String name, Object value) {
        this.attributes.add(new GenericResponseAttribute(name, value));
    }
}

And the class GenericResponseAttribute:

@XStreamAlias("attribute")
public class GenericResponseAttribute {

    private String name;
    private Object value;

    public GenericResponseAttribute(String name, Object value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return this.name;
    }

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

    public Object getValue() {
        return this.value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

And as you can read, every class has their XStream annotations for aliasing and implicit lists, so, let me show you the custom converter I have made for the GenericResponseAttribute class:

public class GenericResponseAttributeConverter implements Converter {

    @Override
    public boolean canConvert(Class type) {
        return type.equals(GenericResponseAttribute.class);
    }

    @Override
    public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext mc) {
        GenericResponseAttribute value = (GenericResponseAttribute)o;
        writer.startNode(value.getName());
        mc.convertAnother(value.getValue());
        writer.endNode();
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext uc) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

So, if I build a GenericResponseObject on runtime, and parse it to XML with my static method, K get something like:

response = new GenericResponseObject();
response.addAttribute("user", "alex");
response.addAttribute("otherAtt", "TEST");
System.out.println(XMLUtil.entityToXML(response));

The result of the println() function is:

<objectResult>
    <attribute>
        <user>hugo</user>
    </attribute>
    <attribute>
        <otherAtt>TEST</otherAtt>
    </attribute>
</objectResult>

Which is almost what I want, but I really need to omit the root element on the GenericResponseAttribute class, its important to say that for the root node that I have shown above, there is always to exist only one node, the attribute name with the content of the attribute value. So, it is going to be a root element always if I delete the current one, for example, the result I need for the previos XML is:

<objectResult>
    <user>hugo</user>
    <otherAtt>TEST</otherAtt>
</objectResult>

My need is pretty basic, but I don't know how to get it right, I have searched and it seems there's not a method like deletRootNode() or replaceRootNode() in the HierarchicalStreamWriter class, and there's no @XStreamNoRoot or @XStreamMakeRoot annotations that I could use in the GenericResponseAttribute, so, thats why I'm asking here, please help me if you know how to do this.

Thanks.

Hugo Allexis Cardona
  • 1,382
  • 19
  • 25

2 Answers2

2

By the moment I ended up using a String replacement after parsing the object to XML, I mean:

public static String entityToXML(Object entity) {
    XStream xstream = new XStream(new StaxDriver());

    xstream.autodetectAnnotations(true);
    xstream.registerConverter(new GenericResponseAttributeConverter());
    String xml = xstream.toXML(entity);

    xml = xml.replace("<attribute>", "");
    xml = xml.replace("</attribute>", "");

    return xml;
}

But, that's not a pretty cool solution, if you have another one please tell me.

Thanks.

Hugo Allexis Cardona
  • 1,382
  • 19
  • 25
0

The solution i propose is based on a modification to the class structure, because i couldn't find a way to vary the fields dynamically, and i think that's not a good practice. Actually, the xml structure must be always the same, but you can skip some fields setting them to null in your POJO

In my solution i altered the two classes of the problem, and at the end there is an example of how to map the xml to the corresponding object and edit the object adding an extra attribute and then mapping it to a xml string with the new attribute

Here is my solution:

    @XStreamAlias("attribute")
    class GenericResponseAttribute {

        @XStreamAlias("user")
        private String user;

        @XStreamAlias("otherAtt")
        private String otherAtt;

        public GenericResponseAttribute(String user, String otherAttr) {
            super();
            this.user = user;
            this.otherAtt = otherAttr;
        }



    }

    @XStreamAlias("objectResult")
    class GenericResponseObject {

        @XStreamAlias("attributes")
        @XStreamImplicit
        private ArrayList<GenericResponseAttribute> attributes;

        public GenericResponseObject() {
            this.attributes = new ArrayList<>();
        }

        public void addAttribute(String name, String otherAttr) {
            this.attributes.add(new GenericResponseAttribute(name, otherAttr));
        }
    }

    String xml=""+
            "<objectResult>"+
            "   <attribute>"+
            "       <user>hugo</user>"+
            "   </attribute>"+
            "   <attribute>"+
            "       <otherAtt>TEST</otherAtt>"+
            "   </attribute>"+
            "</objectResult>";

    XStream stream=new XStream();
    XStream.setupDefaultSecurity(stream);
    stream.autodetectAnnotations(true);
    stream.addPermission(AnyTypePermission.ANY);
    stream.autodetectAnnotations(true);
    stream.allowTypes(new Class[] {GenericResponseObject.class});
    stream.addImplicitCollection(GenericResponseObject.class, "attributes", GenericResponseAttribute.class);
    stream.alias("objectResult", GenericResponseObject.class);
    GenericResponseObject obj = (GenericResponseObject) stream.fromXML(xml);
    obj.addAttribute("another", "3");
    String xml2 = stream.toXML(obj);
    System.out.println(xml2);