简体   繁体   中英

Polymorphic XML Binding with JAXB

Here is what I am trying to do:

XML is this:

<Doctype1>
    <Outter>
        <Inner>
          <ProblemType>
              <foo>...</foo>
              <baz>...</baz>
          </ProblemType>
        </Inner>
    </Outter>
</Doctype1>

<-- However, I have: -->

<Doctype2>
    <Outter>
        <Inner>
          <ProblemType>
              <blah>...</blah>
              <whatever>...</whatever>
          </ProblemType>
        </Inner>
    </Outter>
</Doctype1>

and Share most of the same fields, but in the case of Doctype1 I need ProblemType1 and in Doctype2 I need ProblemType2.

I want to be able to reuse the classes I bind to for and as they are a common tag across all doc types.

@XmlAccessorType(XmlAccessType.FIELD)
public class Doc1{

    @XmlElement(name = "Outter")
    public List<Outter> outtards;

}

@XmlAccessorType(XmlAccessType.FIELD)
public class Outter {

    @XmlElement(name = "Inner")
    public List<Innard> innards;
}

@XmlAccessorType(XmlAccessType.FIELD)
public class Innard{

    // need to change this if it's Doc1 or Doc2. 
    //The subtype of problem type needs to change based on DocX
    // the element type name won't change
    @XmlElement(name = "Inner")
    public ProblemType subtype; 
}

It seems like Maybe a factory is in order?

@XmlType(factoryClass= , factoryMethod=) 

First of all, how would you implement such a structure in Java? There's some distance between DoctypeX and ProblemTypeX , so you can't polyform directly.

Probably with a structure like (very pseudo-code):

class AbstractDoctype {
  AbstractOuter getOuter();
}
class AbstractOuter {
  AbstractInner getInner();
}
class AbstractInner {
  AbstractProblemType getProblemType();
}
class AbstractProblemType {
}

class Doctype1 extends AbstractDoctype  {
  Outer1 getOuter();
}
class Outer1 extends AbstractOuter {
  Inner1 getInner();
}
class Inner1 extends AbstractInner {
  ProblemType1 getProblemType();
}
class ProblemType1 extends AbstractProblemType {
}

class Doctype2 extends AbstractDoctype  {
  Outer2 getOuter();
}
class Outer2 extends AbstractOuter {
  Inner2 getInner();
}
class Inner2 extends AbstractInner {
  ProblemType2 getProblemType();
}
class ProblemType2 extends AbstractProblemType {
}

This kind of structure will be then suitable for your XML. You can probably avoid Outer1 / Outer2 using an @XmlElementWrapper , but you'll still need Inner1 / Inner2 .

  @XmlAccessorType(XmlAccessType.NONE)
    public class Doc1 {

        @XmlElement(type = Outter1.class, name = "Outter")
        private List<Outter> outters;

        public static class Outter1 extends Outter {

            @Override
            @XmlElement(type = Inner1.class, name = "Inner")
            public List<Inner> getInner() {
                return super.getInner();
            }

            @Override
            public void setInner(List<Inner> innards) {
                super.setInner(innards);
            }

            public static class Inner1 extends Inner<ProblemType1> {

                @Override
                @XmlElement(type = ProblemType1.class, name = "ProblemType")
                public List<ProblemType> getProblemTypes() {
                    return super.getProblemTypes();
                }

                @Override
                public void setProblemTypes(List<ProblemType> problemTypes) {
                    super.setProblemTypes(problemTypes);
                }
            }
        }
    }

other class

public class Doc2 {

    @XmlElement(type = Outter2.class, name= "Outter")
    private List<Outter> outters;

    public static class Outter2 extends Outter {

        @Override
        @XmlElement(type = Outter2.class, name = "Inner")
        public List<Inner> getInner() {
            return super.getInner();
        }

        @Override
        public void setInner(List<Inner> innards) {
            super.setInner(groups);
        }

        public static class Inner1 extends Inner<ProblemType2> {
            @Override
            @XmlElement(type = ProblemType2.class, name = "ProblemType")
            public List<ProblemType> getProblemTypes() {
                return super.getProblemTypes();
            }

            @Override
            public void setProblemTypes(List<ProblemType> transactions) {
                super.setProblemTypes(transactions);
            }
        }
    }
}

I have spent some time trying to reduce it but it doesn't seem to respond to XmlAccesorType.FIELD. If I use the same property name as the super or not it doesn't matter.

This is a follow-up to this answer in an attempt to save few lines with the @XmlElementWrapper . Since the Outter class seems to have no meaning on its own and it serves only as a container for the Inner class, it may be avoided:

Please try the following:

@XmlAccessorType(XmlAccessType.NONE)
public class Doc1 {

        @XmlElementWrapper(name = "Outter")
        @XmlElement(type = Inner1.class, name = "Inner")
        public List<Inner> getInner() {
            return super.getInner();
        }

        @Override
        public void setInner(List<Inner> innards) {
            super.setInner(innards);
        }

        public static class Inner1 extends Inner<ProblemType1> {

            @Override
            @XmlElement(type = ProblemType1.class, name = "ProblemType")
            public List<ProblemType> getProblemTypes() {
                return super.getProblemTypes();
            }

            @Override
            public void setProblemTypes(List<ProblemType> problemTypes) {
                super.setProblemTypes(problemTypes);
            }
        }
}

However mind that this now a single list, not a list-of-lists. So this must be still possible with this construct:

<Doctype1>
    <Outter>
        <Inner>
          <ProblemType .../>
        </Inner>
        <Inner>
          <ProblemType .../>
        </Inner>
    </Outter>
</Doctype1>

But this is not:

<Doctype1>
    <Outter>
        <Inner>
          <ProblemType .../>
        </Inner>
    </Outter>
    <Outter>
        <Inner>
          <ProblemType .../>
        </Inner>
    </Outter>
</Doctype1>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM