Search code examples
javaxmlparsingxml-parsingsimple-framework

SimpleXML: element with elements list or text


I must parse a XML file that can be two types:

<property>
    <value>Some text</value>
</property>

and

<property>
    <value>
        <item id="first_id"/>
        <item id="second_id"/>
        <item id="third_id"/>
    </value>
</property>

How can I do this with Java?

I created a class:

@Root(strict = false)
public class PropertyValue {
    @ElementList(inline = true, required = false)
    private List<ItemData> items;

    @Text(required = false)
    private String text;
}

ItemData is item class.

But this does not work. The code gives me an exception:

org.simpleframework.xml.core.TextException: Text annotation @org.simpleframework.xml.Text(data=false, empty=, required=false) on field 'text' private java.lang.String PropertyValue.text used with elements in class PropertyValue

Solution

  • I solved the problem!

    I used the following question answer: Deserializing an XML tag with text AND subtags using Retrofit

    I created a class that convert the XML files as I want (sorry for my code :-( ):

    public class PropertyValueConverter implements Converter<PropertyValue> {
        @Override
        public PropertyValue read(InputNode node) throws Exception {
            PropertyValue propertyValue = new PropertyValue();
            List<ItemData> propertyValueItems = new ArrayList<>();
            String propertyValueText = "";
    
            InputNode itemNode = node.getNext("item");
            while (itemNode != null) {
                String itemId = itemNode.getAttribute("id").getValue();
                ItemData itemData = new ItemData();
                itemData.setId(itemId);
                propertyValueItems.add(itemData);
                itemNode = node.getNext("id");
            }
    
            if (propertyValueItems.size() == 0) {
                propertyValueText = node.getValue();
            }
    
            propertyValue.setItems(propertyValueItems);
            propertyValue.setText(propertyValueText);
    
            return propertyValue;
        }
    
        @Override
        public void write(OutputNode node, PropertyValue value) throws Exception {
    
        }
    }
    

    Then I changed PropertyValue class:

    @Root(strict = false)
    @Convert(value = PropertyValueConverter.class)
    public class PropertyValue {
        private List<ItemData> items;
    
        private String text;
    
        public List<ItemData> getItems() {
            return items;
        }
    
        public void setItems(List<ItemData> items) {
            this.items = items;
        }
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    }
    

    Then I set SimpleXml converter factory:

    private static Strategy strategy = new AnnotationStrategy();
    private static Serializer serializer = new Persister(strategy);
    
    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(SimpleXmlConverterFactory.create(serializer));
    

    So, It works for me.