Lakatos Gyula
Lakatos Gyula

Reputation: 4160

JAXB @XmlElementWrapper ArrayList size zero

I have an XML to load with JAXB. It load correctly, except that getEffectSettings() size is 0 when it should show 2 because I have two 'effect-setting' in the 'settings-list' element. Any ideas? I'm able to load and print the Effect's target attribute and the effects are shown as it should except this variable.

The XML:

<item:item xmlns:item="http://www.swordsandsorcery.com/item" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.swordsandsorcery.com/item ../schema.xsd">
    <item:id>1</item:id>
    <item:name>Laxi's epic</item:name>
    <item:type>ONE_HANDED_AXES</item:type>
    <item:usable>true</item:usable>
    <item:use-effect-list>
        <item:effect target="com.xyz.HealEffect">
            <item:settings-list>
                <item:effect-setting>
                    <item:setting-name>amount</item:setting-name>
                    <item:setting-value>10</item:setting-value>
                </item:effect-setting>
                <item:effect-setting>
                    <item:setting-name>xyz</item:setting-name>
                    <item:setting-value>10</item:setting-value>
                </item:effect-setting>
            </item:settings-list>
        </item:effect>
    </item:use-effect-list>
    <item:weight>5</item:weight>
    <item:combat-modifiers>
        <item:modifier>
            <item:attribute>ATTACK</item:attribute>
            <item:amount>1</item:amount>
        </item:modifier>
        <item:modifier>
            <item:attribute>DEFENSE</item:attribute>
            <item:amount>1</item:amount>
        </item:modifier>
        <item:modifier>
            <item:attribute>LIFE</item:attribute>
            <item:amount>3</item:amount>
        </item:modifier>
    </item:combat-modifiers>
    <item:general-modifiers>
        <item:modifier>
            <item:attribute>STRENGTH</item:attribute>
            <item:amount>1</item:amount>
        </item:modifier>
    </item:general-modifiers>
    <item:skill-modifiers>
        <item:modifier>
            <item:attribute>TWO_HANDED_CRUSHING_WEAPONS</item:attribute>
            <item:amount>2</item:amount>
        </item:modifier>
    </item:skill-modifiers>
    <item:basic-modifiers>
        <item:modifier>
            <item:attribute>MOVEMENT</item:attribute>
            <item:amount>1</item:amount>
        </item:modifier>
    </item:basic-modifiers>
    <item:general-requirements>
        <item:requirement>
            <item:attribute>DEXTERITY</item:attribute>
            <item:amount>100</item:amount>
        </item:requirement>
    </item:general-requirements>
</item:item>

The nodes in path:

RawItemDefinition.java (root node): package com.morethanheroic.swords.item.service.domain;

import com.morethanheroic.swords.effect.domain.Effect;
import com.morethanheroic.swords.item.domain.ItemType;

import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@XmlRootElement(name = "item")
@XmlAccessorType(XmlAccessType.FIELD)
public class RawItemDefinition {

    private int id;
    private String name;
    private ItemType type;
    private boolean usable;
    private int weight;

    @XmlElementWrapper(name = "use-effect-list")
    @XmlElement(name = "effect")
    private ArrayList<Effect> effectList;

    @XmlElementWrapper(name = "basic-modifiers")
    @XmlElement(name = "modifier")
    private ArrayList<BasicAttributeModifierDefinition> basicModifiers;

    @XmlElementWrapper(name = "combat-modifiers")
    @XmlElement(name = "modifier")
    private ArrayList<CombatAttributeModifierDefinition> combatModifiers;

    @XmlElementWrapper(name = "general-modifiers")
    @XmlElement(name = "modifier")
    private ArrayList<GeneralAttributeModifierDefinition> generalModifiers;

    @XmlElementWrapper(name = "skill-modifiers")
    @XmlElement(name = "modifier")
    private ArrayList<SkillAttributeModifierDefinition> skillModifiers;

    @XmlElementWrapper(name = "basic-requirements")
    @XmlElement(name = "requirement")
    private ArrayList<BasicAttributeRequirementDefinition> basicRequirements;

    @XmlElementWrapper(name = "combat-requirements")
    @XmlElement(name = "requirement")
    private ArrayList<CombatAttributeRequirementDefinition> combatRequirements;

    @XmlElementWrapper(name = "general-requirements")
    @XmlElement(name = "requirement")
    private ArrayList<GeneralAttributeRequirementDefinition> generalRequirements;

    @XmlElementWrapper(name = "skill-requirements")
    @XmlElement(name = "requirement")
    private ArrayList<SkillAttributeRequirementDefinition> skillRequirements;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public ItemType getType() {
        return type;
    }

    public int getWeight() {
        return weight;
    }

    public boolean isUsable() {
        return usable;
    }

    public String toString() {
        return "RawItemDefinition -> [id: " + id + " name: " + name + "]";
    }

    public List<BasicAttributeModifierDefinition> getBasicModifiers() {
        return basicModifiers;
    }

    public List<CombatAttributeModifierDefinition> getCombatModifiers() {
        return combatModifiers;
    }

    public List<GeneralAttributeModifierDefinition> getGeneralModifiers() {
        return generalModifiers;
    }

    public List<SkillAttributeModifierDefinition> getSkillModifiers() {
        return skillModifiers;
    }

    public List<SkillAttributeRequirementDefinition> getSkillRequirements() {
        return skillRequirements;
    }

    public List<BasicAttributeRequirementDefinition> getBasicRequirements() {
        return basicRequirements;
    }

    public List<CombatAttributeRequirementDefinition> getCombatRequirements() {
        return combatRequirements;
    }

    public List<GeneralAttributeRequirementDefinition> getGeneralRequirements() {
        return generalRequirements;
    }

    public List<AttributeRequirementDefinition> getAllRequirements() {
        List<AttributeRequirementDefinition> list = new ArrayList<>();

        if (basicRequirements != null) {
            list.addAll(basicRequirements);
        }
        if (combatRequirements != null) {
            list.addAll(combatRequirements);
        }
        if (generalRequirements != null) {
            list.addAll(generalRequirements);
        }
        if (skillRequirements != null) {
            list.addAll(skillRequirements);
        }

        return Collections.unmodifiableList(list);
    }

    public ArrayList<Effect> getEffectList() {
        return effectList;
    }
}

Effect.java: package com.morethanheroic.swords.effect.domain;

import javax.xml.bind.annotation.*;
import java.util.ArrayList;

@XmlAccessorType(XmlAccessType.FIELD)
public class Effect {

    @XmlAttribute
    private String target;

    @XmlElementWrapper(name = "settings-list")
    @XmlElement(name = "effect-setting")
    private ArrayList<EffectSetting> effectSettings;

    public String getTarget() {
        return target;
    }

    public ArrayList<EffectSetting> getEffectSettings() {
        return effectSettings;
    }
}

EffectSetting.java:

package com.morethanheroic.swords.effect.domain;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@XmlAccessorType(XmlAccessType.FIELD)
public class EffectSetting {

    @XmlElement(name = "setting-name")
    private String name;

    @XmlElement(name = "setting-value")
    private String value;

    public String getName() {
        return name;
    }

    public String getValue() {
        return value;
    }
}

The unmarshaller I use is this:

package com.morethanheroic.swords.definition.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class XMLDefinitionLoader {

    private static final SchemaFactory schemaFactory =  SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

    @Autowired
    private ApplicationContext applicationContext;

    public List loadDefinitions(Class clazz, String resourcePath, String schemaPath) throws JAXBException, IOException, SAXException {
        return unmarshallTargetFiles(buildUnmarshaller(clazz, schemaPath), getTargetFiles(resourcePath));
    }

    private ArrayList unmarshallTargetFiles(Unmarshaller unmarshaller, File[] files) throws JAXBException {
        ArrayList list = new ArrayList<>();

        for (File file : files) {
            list.add(unmarshaller.unmarshal(file));
        }

        return list;
    }

    private File[] getTargetFiles(String resourcePath) throws IOException {
        return applicationContext.getResource(resourcePath).getFile().listFiles();
    }

    private Unmarshaller buildUnmarshaller(Class clazz, String schemaPath) throws IOException, SAXException, JAXBException {
        Unmarshaller unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
        unmarshaller.setSchema(buildSchema(schemaPath));

        return unmarshaller;
    }

    private Schema buildSchema(String schemaPath) throws IOException, SAXException {
        return schemaFactory.newSchema(applicationContext.getResource(schemaPath).getFile());
    }
}

And this is how I try to access the effects:

@PostConstruct
public void init() throws Exception {
    List<RawItemDefinition> rawItemDefinitionList = xmlDefinitionLoader.loadDefinitions(RawItemDefinition.class, "classpath:data/item/definition/", "classpath:data/item/schema.xsd");

    for (RawItemDefinition rawItemDefinition : rawItemDefinitionList) {
        if(rawItemDefinition.getEffectList() != null) {
            System.out.println("EFFECT: "+rawItemDefinition.getEffectList().get(0).getTarget());
            System.out.println("EFFECT: "+rawItemDefinition.getEffectList().get(0).getEffectSettings().size());
        }

        itemDefinitionMap.put(rawItemDefinition.getId(), new ItemDefinition(rawItemDefinition));
    }
}

Upvotes: 0

Views: 952

Answers (1)

Ga&#235;l J
Ga&#235;l J

Reputation: 15275

Could you provide your code to call getEffectSettings() and how the unmarshalling is done (by hand or any framework ?).

UPDATE

Have you tried adding @XmlRootElement to your EffectSetting class ?

UPDATE 2

As you found yourself, check packages of your classes :

The Effect and EffectSetting was in an other package than RawItemData and I added the package-info.java to the RawItemData's package. After adding it to the Effect's package it's started working.

Upvotes: 1

Related Questions