views:

743

answers:

4

I have an object that needs to be serialised as XML, which contains the following field:

List<String> tags = new List<String>();

XStream serialises it just fine (after some aliases) like this:

<tags>
  <string>tagOne</string>
  <string>tagTwo</string>
  <string>tagThree</string>
  <string>tagFour</string>
</tags>

That's OK as far as it goes, but I'd like to be able to rename the <string> elements to, say, <tag>. I can't see an obvious way to do that from the alias documentation on the XStream site. Am I missing something obvious?

+1  A: 

Add alias for the java.util.String class. Okay, that may break something else elsewhere but in this exact case that should be enough.

If you don't want to do the thing above, you can make your own converters (see this handy tutorial) which will help you achieve your goal. And don't be afraid of making your own converter either, they're really easy to implement.

Esko
Gotcha. I'd hoped to avoid writing my own Converter, since the class in question has a tonne of fields, but if that's what I have to do...Thanks. :)
Will Goring
+2  A: 

Out of interest I gave it a try to do it without writing my own converter. Basically I just register a special instructed version of CollectionConverter for a certain field in a certain class.

Relevant snippet:

ClassAliasingMapper mapper = new ClassAliasingMapper(xstream.getMapper());
mapper.addClassAlias("tag", String.class);
xstream.registerLocalConverter(
    Test.class,
    "tags",
    new CollectionConverter(mapper)
);

Full-blown example:

import com.thoughtworks.xstream.*;
import com.thoughtworks.xstream.converters.collections.*;
import com.thoughtworks.xstream.mapper.*;
import java.util.*;

public class Test {
    public List<String> tags = new ArrayList<String>();
    public List<String> notags = new ArrayList<String>();
    public Test(String tag, String tag2) {
        tags.add(tag); tags.add(tag2);
        notags.add(tag); notags.add(tag2);
    }
    public static void main(String[] args) {
        Test test = new Test("foo", "bar");
        XStream xstream = new XStream();

        ClassAliasingMapper mapper = new ClassAliasingMapper(xstream.getMapper());
        mapper.addClassAlias("tag", String.class);
        xstream.registerLocalConverter(
            Test.class,
            "tags",
            new CollectionConverter(mapper)
        );

        System.out.println(xstream.toXML(test));
    }
}


Not tested but this should work. No?

xstream.alias("tag", java.lang.String.class);
jitter
It does work (I just tried it,) but it picks up and aliases the members of any <em>other</em> Collection of Strings in the class as well. Unfortunately in my real class (unlike the example) there is more than one such Collection.
Will Goring
This solution works perfectly. Tested on XStream 1.3.1.Will > how is it possible? this kind of aliasing is done only on field "tags" of Test class and every other collection in Test class is aliased by default settings of XStream mappers (ie. they have same xml representation as you write in your initial post -> <tags> <string>...</string></tags>).
Michal Bernhard
Will Gorings comment relates to the first version of my answer. Which is the oneliner below the horizontal line
jitter
+2  A: 

I'd suggest changing the List<String> to a List<Tag>, where Tag is a domain object that essentially just contains a String. Then you say:

xstream.alias("tag", org.goring.Tag.class);

and you get exactly what you want. This avoids having to roll your own Converter.

Jim Ferrans
That's exactly what I ended up doing about half an hour ago. Worked a treat. :)
Will Goring
A: 

(i now it's a bit old...)

but, after have changed 'strings' pattern, if i want to get rid of <tags>, how can this be done? I've this:

<tags>
  <tag>tagOne</tag>
  <tag>tagTwo</tag>
  <tag>tagThree</tag>
  <tag>tagFour</tag>
</tags>

and i want this

  <tag>tagOne</tag>
  <tag>tagTwo</tag>
  <tag>tagThree</tag>
  <tag>tagFour</tag>

Speaking in general, i need to map into java object a structure like this:

<root>
  ...
  <tag>tagOne</tag>
  <tag>tagTwo</tag>
  <tag>tagThree</tag>
  <tag>tagFour</tag>
  ...
</root>

but after few tests i wasn't able to. The best was:

<root>
  ...
  <tags>
    <tag>tagOne</tag>
    <tag>tagTwo</tag>
    <tag>tagThree</tag>
    <tag>tagFour</tag>
  </tags>
  ...
</root>

and if i use implicit collection to get rid of <tags> every single tag came back to <string>.

What i'm missing?

andrew
this is similar to my issue http://stackoverflow.com/questions/3216943/xstream-flatten-an-object
brian_d