Q: How to unmarshal xml with repetitive entries using JAXB and Spring I have to convert xml to Map<String,String>. I have following XML structure:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Environments>
  <Environment Name="A" URIPath="http://a.com" />
  <Environment Name="B" URIPath="http://b.com" />
  <Environment Name="C" URIPath="http://c.com" />
</Environments> 

I tried multiple ways but eneded with Class has two properties of the same name "URIPath". What is the right design for unmarshalling this XML?
UPDATE:
Using provided solution #1 I am getting :
Class has two properties of the same name "environments"
    this problem is related to the following location:
        at public java.util.List app.model.Environments.getEnvironments()
        at app.model.Environments
    this problem is related to the following location:
        at public java.util.List app.model.Environments.environments
        at app.model.Environments
Class has two properties of the same name "URIPath"
    this problem is related to the following location:
        at public java.lang.String app.model.Environment.getURIPath()
        at app.model.Environment
        at public java.util.List app.model.Environments.environments
        at app.model.Environments
    this problem is related to the following location:
        at java.lang.String app.model.Environment.URIPath
        at app.model.Environment
        at public java.util.List app.model.Environments.environments
        at app.model.Environments
] with root cause


A: You have 2 options:
1) Unmarshal a collection of Environment instances with 2 fields: Name and URIPath. You can build the map later if you want to from the collection.
2) Use a custom XmlAdapter which properly creates the map from the collection.
Elaborating Solution #1
This solution needs the following classes:
class Environments {
    @XmlElement(name = "Environment")
    public List<Environment> environments;
}

class Environment {
    @XmlAttribute(name = "Name")
    public String Name;
    @XmlAttribute(name = "URIPath")
    public String URIPath;
}

And using these, unmarhaling:
Environments environments = JAXB.unmarshal(new File("env.xml"),
    Environments.class);

Elaborating Solution #2
If you want to use a custom XmlAdapter to directly get a Map, the XML input in its current form cannot be used. It has to be slightly modified to put a wrapper XML element around it. This is required because in Java the Map is a property of a class but the <Environments> tag is just the wrapper for the Map. Example modified XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<wrapper>
  <Environments>
    <Environment Name="A" URIPath="http://a.com" />
    <Environment Name="B" URIPath="http://b.com" />
    <Environment Name="C" URIPath="http://c.com" />
  </Environments>
</wrapper>

Taking this as the input XML, here is the solution:
class EnvironmentMap {
    @XmlJavaTypeAdapter(value = EnvMapAdapter.class)
    @XmlElement(name = "Environments")
    public Map<String, String> envMap;
}

class Environments {
    @XmlElement(name = "Environment")
    public List<Environment> environments;
}

class Environment {
    @XmlAttribute(name = "Name")
    public String name;
    @XmlAttribute(name = "URIPath")
    public String uriPath;
}

class EnvMapAdapter extends XmlAdapter<Environments, Map<String, String>> {
    @Override
    public Map<String, String> unmarshal(Environments envs) throws Exception {
        Map<String, String> map = new HashMap<>();
        for (Environment env : envs.environments)
            map.put(env.name, env.uriPath);
        return map;
    }

    @Override
    public Environments marshal(Map<String, String> map) throws Exception {
        Environments environments = new Environments();
        // This method is only called if you marshal (Java -> XML)
        environments.environments = new ArrayList<>(map.size());

        for (Entry<String, String> entry : map.entrySet()) {
            Environment e = new Environment();
            e.name = entry.getKey();
            e.uriPath = entry.getValue();
            environments.environments.add(e);
        }

        return environments;
    }
}

And using it:
EnvironmentMap envMap = JAXB.unmarshal(new File("env2.xml"),
    EnvironmentMap.class);
System.out.println(envMap.envMap);

Which prints:
{A=http://a.com, B=http://b.com, C=http://c.com}

