Search code examples
javaxstream

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


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;
    }

}

Solution

  • 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;
        }
    
    }