6

I'm currently working on replacing a legacy system with JAXB and I'm running into problem with parsing the XML. The number one requirement of the system is that it must be a drop-in replacement so I cannot modify the format of the XML. Below is the XML section that is giving me trouble.

<xx>
    <s1>
        <X>-9999</X>
        <Y>-9999</Y>
    </s1>
    <s2>
        <X>-9999</X>
        <Y>-9999</Y>
   </s2>
</xx>

The issue with the XML is that all of the s# objects are the exact same and there can be up to 256 of them. Is there a way in JAXB to annotate such a tag or do I have to create 256 separate annotations? Any help would be most appreciated.

Here is the java code for the xx object. Note: the object was originally programmed with the understanding that there would only be 2 s# objects, but that since has changed.

@XmlRootElement(name="xx")

public class XMLXx implements Serializable {

    private static final long serialVersionUID = 4064597372833234503L;

    private XMLSite siteOne;
    private XMLSite siteTwo;

    @XmlElement(name="s1")
    public XMLSite getSiteOne() {
        return siteOne;
    }

    public void setSiteOne(XMLSite s1) {
        this.siteOne = s1;
    }

    @XmlElement(name="s2")
    public XMLSite getSiteTwo() {
        return siteTwo;
    }

    public void setSiteTwo(XMLSite s2) {
        this.siteTwo = s2;
    }
}

And here is the XMLSite object:

public class XMLSite implements Serializable {

    private static final long serialVersionUID = -4374405403222014476L;

    private Integer x;
    private Integer y;

    @XmlElement(name="X")
    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    @XmlElement(name="Y")
    public Integer getY() {
        return y;
    }

    public void setY(Integer y) {
        this.y = y;
    }
}
Chris Flynn
  • 953
  • 6
  • 11

4 Answers4

7

If you want to handle at the s# items as a collection:

import java.io.Serializable;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="xx")
public class XMLXx implements Serializable {

    private static final long serialVersionUID = 4064597372833234503L;

    private List<XMLSite> sites;

    @XmlElement(name="s")
    public List<XMLSite> getSites() {
        return sites;
    }

    public void setSites(List<XMLSite> sites) {
        this.sites = sites;
    }

}

Then you could do something like to fool JAXB into thinking all the elements (s1, s2, etc) are actually called s:

import java.io.FileInputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(XMLXx.class);

        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml"));
        xsr = new SiteStreamReaderDelegate(xsr);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        XMLXx object = (XMLXx) unmarshaller.unmarshal(xsr);
        System.out.println(object.getSites().size());

    }

    private static class SiteStreamReaderDelegate extends StreamReaderDelegate {

        public SiteStreamReaderDelegate(XMLStreamReader xsr) {
            super(xsr);
        }

        @Override
        public String getLocalName() {
            String localName = super.getLocalName();
            if(localName.startsWith("s")) {
                return "s";
            }
            return localName;
        }

    }
}

For a similar example see:

bdoughan
  • 147,609
  • 23
  • 300
  • 400
4

No, I don't think so, not with standard JAXB. You could, in principle use @XmlMixed, but you'd still end up with a bunch of DOM Element objects, not bound classes. Some proprietary JAXB extension such as MOXy might be able to handle it, though.

This isn't really a good use case for JAXB. As you say, the XML is poorly designed. You'd be better off parsing this by hand (using e.g. STAX or DOM), and building the desired object model yourself.

skaffman
  • 398,947
  • 96
  • 818
  • 769
3

JaxB does not support "dynamic" tags. Since can only be 256 of these, use a script to generate source.

Andrew White
  • 52,720
  • 19
  • 113
  • 137
0

Write a meta-XSD in freemarker/velocity or the like. It can define the 256 types as subtypes of some parent type using a for loop. If you want to fully automate, you can write a maven plugin to generate the XSD from the freemarker and the run generate-sources on the result.

jiggy
  • 3,828
  • 1
  • 25
  • 40