XStream and using 'ToAttributedValueConverter' for two similar but different XML fields

StackOverflow https://stackoverflow.com/questions/20694210

  •  19-09-2022
  •  | 
  •  

Question

In my XML files I have lines like this;

    <text id="name">Bu ilk rule</text>
    <text id="bundleName">Rule.behavior</text>

So my Text class is like this:

@XStreamConverter(value = ToAttributedValueConverter.class, strings = {"value"})
@XStreamAlias("text")
public class Text {

    @XStreamAsAttribute
    String id = "unset";
    String value = "0";
}

And it works. But today I got this in an XML:

        <text id="targetAttribute">game.devices.touches.touch1.y</text>
        <text id="RHS">
          <expression>
            <string>acos(x)</string>
          </expression>
        </text>

And it does not work. If I remove the @XStreamConverter I get an output like this:

        <text id="targetAttribute"/>
        <text id="RHS">
          <expression>
            <string>acos(x)</string>
          </expression>
        </text>

What is the workaround for this?

Edit:

After Matthias's answer the closest I can get to desired format is this:

        <text id="RHS">
          <expression>
            <string>acos(x)</string>
          </expression>
        </text>

But this method fails, it starts not showing the "value" field:

        <text id="name"/>
        <text id="bundleName"/>
        <text id="targetAttribute"/>

With this class:

@XStreamAlias("text")

class Test {

    @XStreamAsAttribute
    String id = "unset";
    String value = "0";
    myString string = new myString();
    Expression expression = new Expression();

}

Also, I do the reading like this, so it must work when doing this:

String xml1 = readFile("C:\\1.xml");
Actor actorNew = (Actor) xstream.fromXML(xml1);

String xmlNew = xstream.toXML(actorNew);
System.out.println(xmlNew);

Edit2:

myString classs for <string> in XML:

@XStreamConverter(value = ToAttributedValueConverter.class, strings = {"value"})
public class myString {

    @XStreamAlias("string")
    String value = "";

}

And here is a zoomed-out view of the XML I'm trying to read:

<behaviors>
        <behavior id="id384781" class="ChangeAttributeAction" enabled="true">
          <attributes>
            <text id="name">Change Attribute</text>
            <text id="bundleName">ChangeAttribute.behavior</text>
            <text id="targetAttribute">game.devices.touches.touch1.y</text>
            <text id="RHS">
              <expression>
                <string>acos(x)</string>
              </expression>
            </text>
          </attributes>
        </behavior>
</behaviors>

All is working until the text with id = "RHS"

Edit3:

I tried something like that on custom converter but didn't work:

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
    Test test = new Test();
    test.expression = new Expression();
    test.expression.string = new myString();
    test.value = reader.getValue();
    test.id = reader.getAttribute("id");
    reader.moveDown();
    reader.moveDown();
    test.expression.string.value = reader.getValue();
    return test;
}

Edit4

WOOOOHOOOOOOOOOOOOOOOO I GOT IT AT LAST!

Here is how I did it:

class ValueConverter implements Converter {

    @Override
    public boolean canConvert(Class type) {
        return Test.class.isAssignableFrom(type);
    }

    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        Test src = (Test) source;
        writer.addAttribute("id", src.id);
        writer.setValue(src.value);

        if (src.expression.string.value.length() > 0) {
            writer.startNode("expression");
            writer.startNode("string");
            writer.setValue(src.expression.string.value);
            writer.endNode();
        }

    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Test test = new Test();
        test.id = reader.getAttribute("id");
        test.value = reader.getValue();
        if (reader.hasMoreChildren()) {
            reader.moveDown();
            reader.moveDown();
            test.expression.string.value = reader.getValue();

        }
        return test;
    }

}
Was it helpful?

Solution 2

I did it by changing the converter MAtthias mentioned;

class ValueConverter implements Converter {

    @Override
    public boolean canConvert(Class type) {
        return Test.class.isAssignableFrom(type);
    }

    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        Test src = (Test) source;
        writer.addAttribute("id", src.id);
        writer.setValue(src.value);

        if (src.expression.string.value.length() > 0) {
            writer.startNode("expression");
            writer.startNode("string");
            writer.setValue(src.expression.string.value);
            writer.endNode();
        }

    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        Test test = new Test();
        test.id = reader.getAttribute("id");
        test.value = reader.getValue();
        if (reader.hasMoreChildren()) {
            reader.moveDown();
            reader.moveDown();
            test.expression.string.value = reader.getValue();

        }
        return test;
    }

}

OTHER TIPS

Well, the code you show, cannot produce this XML. I guess somewhere in your real code, there is some inheritance (A class Expression extending your Test class) which is producing this result. Seems as if the ToAttributedValueConverter does not really work for derived classes and gets ignored there.

If you have a more complex object structure which you want to map to a very specific XML representation, you should use converters. In doubt you can always create a custom converter which handles the marshalling / unmarshalling. For those converters, you can easily define for which classes they should be used (see the canConvert(Class) method.

I did this for you for a very similar piece of code:

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

@XStreamAlias("text")
class Test {

  String id = "unset";
  String value = "0";
}

@XStreamAlias("expression")
class Expression extends Test {
  Expression() {
    value = "acos(x)";
  }
}

class ValueConverter implements Converter {

  @Override
  public boolean canConvert(Class type) {
    return Test.class.isAssignableFrom(type);
  }

  @Override
  public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
    Test src = (Test)source;
    writer.addAttribute("id", src.id);
    writer.setValue(src.value);
  }

  @Override
  public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
    Test test = new Test();
    test.value = reader.getValue();
    test.id = reader.getAttribute("id");
    return test;
  }

}

public class Example {
  public static void main(String[] args) {
    XStream xStream = new XStream();
    xStream.autodetectAnnotations(true);
    System.out.println("BEFORE setting a converter:");
    System.out.println(xStream.toXML(new Test()));
    System.out.println(xStream.toXML(new Expression()));
    xStream.registerConverter(new ValueConverter());
    System.out.println("\nAFTER setting a converter:");
    System.out.println(xStream.toXML(new Test()));
    System.out.println(xStream.toXML(new Expression()));
  }
}

Running this, you get the following output:

<text>
  <id>unset</id>
  <value>0</value>
</text>
<expression>
  <id>unset</id>
  <value>acos(x)</value>
</expression>

AFTER setting a converter:
<text id="unset">0</text>
<expression id="unset">acos(x)</expression>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top