Question

I have the following situtation in deserializing xml using SimpleFramework of specific format that cannot be changed...

<Question ID="Q1">
    THIS INNER TEXT IS THE ISSUE

    <Criteria Type="Normal" Source="OEM">
        <Value Type="0">45.7</Value>
        <Value Type="100">42.7</Value>
    </Criteria>
    <Criteria Type="Impact" Source="OEM">
        <Value Type="0">45.7</Value>
        <Value Type="100">42.7</Value>
    </Criteria>
    <!-- CRITERIA CAN HAVE ANY NUMBER -->

</Question>

and here is the class I wrote for Question

@Root (name="Question")
public class Question {

    @Attribute (name="ID") 
    private String id;

    @ElementList (inline=true, required=false)
    private List<Criteria> criteria;

    @Text
    private String text;

    // And their getter and setters...
}

Now the issue is that, I CANNOT GET INNER TEXT...

Can anybody suggest me the way to do this...???

Was it helpful?

Solution

You can't use @Text annotation here, this is only possible if you don't have any child.

and it [@Text annotation] can not appear with the another XML element annotations, such as the Element annotation.

Source: @Text API documentation

However, you can use a Converter to those text. This is a bit tricky, but here's an example:

Criteria class:

@Root(name = "Criteria")
public class Criteria
{
    @Attribute(name = "Type")
    private String type;
    @Attribute(name = "Source")
    private String source;
    @ElementList(name = "Values", inline = true)
    private ArrayList<Value> values;



    public Criteria(String type, String source)
    {
        this.type = type;
        this.source = source;
        this.values = new ArrayList<>();
    }

    private Criteria() { }


    // ...


    @Override
    public String toString()
    {
        return "Criteria{" + "type=" + type + ", source=" + source + ", values=" + values + '}';
    }


    // Inner class for values - you also can use a normal one instead
    @Root(name = "Value")
    public static class Value
    {
        @Attribute(name = "Type", required = true)
        private int type;
        @Text(required = true)
        private double value;


        public Value(int type, double value)
        {
            this.type = type;
            this.value = value;
        }

        private Value() { }

    } 

}

Question class:

@Root(name = "Question")
@Convert( value = Question.QuestionConvert.class)
public class Question
{
    @Attribute(name = "ID", required = true)
    private String id;
    @Element(name = "text")
    private String text;
    @ElementList(inline = true)
    private ArrayList<Criteria> criteria;


    public Question(String id)
    {
        this.id = id;
        this.criteria = new ArrayList<>();

        this.text = "This inner text ...";
    }

    private Question() { }


    // ...


    @Override
    public String toString()
    {
        return "Question{" + "id=" + id + ", text=" + text + ", criteria=" + criteria + '}';
    }



    static class QuestionConvert implements Converter<Question>
    {
        private final Serializer ser = new Persister();


        @Override
        public Question read(InputNode node) throws Exception
        {
            Question q = new Question();
            q.id = node.getAttribute("ID").getValue();
            q.text = node.getValue();

            q.criteria = new ArrayList<>();
            InputNode criteria = node.getNext("Criteria");

            while( criteria != null )
            {
                q.criteria.add(ser.read(Criteria.class, criteria));
                criteria = node.getNext("Criteria");
            }

            return q;
        }


        @Override
        public void write(OutputNode node, Question value) throws Exception
        {
            node.setAttribute("ID", value.id);
            node.setValue(value.text);


            for( Criteria c : value.getCriteria() )
            {
                ser.write(c, node);
            }
        }
    }
}

Please note all those empty constructors. They are required by simple but you can keep them private. You don't have to implement those inner classes as inner.

The key to the solution is the Converter which allows you to use text and child-elements together. You can use a Serializer to write all the Criteria-childs.

There are some toString() methods, those are only for testing - you can implement them as you need.

Input XML:

<Question ID="Q1">This inner text ...
   <Criteria Type="Normal" Source="OEM">
      <Value Type="0">45.7</Value>
      <Value Type="100">42.7</Value>
   </Criteria>
   <Criteria Type="Impact" Source="OEM">
      <Value Type="0">45.7</Value>
      <Value Type="100">42.7</Value>
   </Criteria>
</Question>

Example code:

Serializer ser = new Persister(new AnnotationStrategy()); // Don't miss the AnnotationStrategy!

Question q = ser.read(Question.class, f);
System.out.println(q);

Output:

Question{id=Q1, text=This inner text ...
   , criteria=[Criteria{type=Normal, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}, Criteria{type=Impact, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}]}

Not very beautiful, but it's working! :-)

Ps. Since both methods of the Converter are implemented you also can use this code to serialize a Question object.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top