如何让JAXB在解组时使用我的一个子类作为非根元素?

时间:2015-09-07 20:11:47

标签: java jaxb

我一直在阅读关于这个主题的所有问题,但没有一个与我的问题有关。我有这些类(每个类都在自己的文件中):

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Root")
public class Root {
    @XmlElements({
        @XmlElement(name="Child", type=ChildImpl.class),
        @XmlElement(name="Child", type=ChildImpl2.class)
    })
    protected List<AbstractChild> children;

    public List<AbstractChild> getChildren() {
        if (children == null) {
            children = new ArrayList<AbstractChild>();
        }
        return this.children;
    }
}

@XmlTransient
public abstract class AbstractChild {
    @XmlElement(name = "Other", required = true)
    protected List<Other> others; // class Other is not important for my question, so I'll skip its implementation details
    public List<Other> getOthers() {
        if (others == null) {
            others = new ArrayList<Other>();
        }
        return this.others;
    }
}

@XmlRootElement(name = "Child")
public class ChildImpl extends AbstractChild {
    // custom behavior
}

@XmlRootElement(name = "Child")
public class ChildImpl2 extends AbstractChild {
    // different custom behavior
}

然后,我有一个执行解组的类:

JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
result = (Root) jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(fileContent)); // where fileContent is an instance of byte[]

现在,根据我们的上下文,我希望unmarshaller为Root对象使用Child的特定实现......但这就是我正在努力的地方:我不知道如何通知unmarshaller使用特定的子类对于Child(在我们的文件中,Root,Child和Other的结构总是相同的。但是,我们如何处理Child取决于每个文件的源文件夹)。 我在创建上下文时尝试传递具体类 - 出于测试目的 - (例如JAXBContext.newInstance(Root.class,ChildImpl.class)),但由于某种原因,unmarshaller总是解析为最后输入的类@XmlElements数组(在本例中为ChildImpl2)。

我也尝试删除@XmlElements注释,但unmarshaller不知道处理Version元素,因为父类是抽象的(这也是我添加@XmlTransient注释的原因;我没有兴趣试图实例AbstractChild)

有什么想法吗? 提前谢谢!

1 个答案:

答案 0 :(得分:1)

如果xml文件中的结构总是相同的,那么使用抽象类AbstractChild是没有意义的(在我看来)。逻辑应该在你的代码中的其他地方,策略或类似的东西中。

但它有可能:

有一个代表孩子的XmlElement。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "child")
public class Child {
    @XmlElement(name = "name")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }
}

有另一个类,如MyAbstractChild

public abstract class MyAbstractChild {
    private final Child child;

    public AbstractChild(final Child child) {
        this.child = child;
    }

    public Child getChild() {
        return child;
    }
}

您想要的每个行为的子类:

public class MyChildImpl extends MyAbstractChild {
    public MyChildImpl(final Child child) {
        super(child);
    }

    // custom behavior here
}

然后你可以实现一个XmlAdapter:

public class MyChildImpAdapter extends XmlAdapter<Child, MyAbstractChild> {
    private final Class<? extends AbstractChild> implClass;

    public MyChildImpAdapter(final Class<? extends MyAbstractChild> implClass){
        this.implClass = implClass;
    }

    @Override
    public MyAbstractChild unmarshal(final Child child) throws Exception {
        if (MyChildImpl.class.equals(this.implClass)) {
            return new MyChildImpl(child);
        } else {
            return new MyChildImpl2(child);
        }
    }

    @Override
    public Child marshal(final MyAbstractChild abstractChild) throws Exception {
        return abstractChild.getChild();
    }
}

现在,您可以在根元素中使用新类型MyAbstractChild:

@XmlJavaTypeAdapter(value = MyChildImpAdapter.class)
protected List<MyAbstractChild> children;

最后,您可以训练unmarshaller使用您选择的适配器:

final JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(MyChildImpAdapter.class, new MyChildImpAdapter(MyChildImpl.class));
final InputStream inputStream = getClass().getResourceAsStream("some.xml");
final Root root = (Root) jaxbUnmarshaller.unmarshal(inputStream);

现在,你的孩子们已经拥有了带有MyChildImpl-Objects的Root-Element。

但正如我在开头写的那样:应该有另一种选择:)