简体   繁体   English

在解组期间,如何使JAXB对非根元素使用我的子类之一?

[英]How to get JAXB to use one of my sub-classes for a non-root element during unmarshalling?

I've been reading all the questions on this subject, but none of them relate to my problem. 我一直在阅读有关该主题的所有问题,但都与我的问题无关。 I have these classes (each of them in their own file): 我有这些类(每个类都在各自的文件中):

@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
}

And then, I have the class that performs the unmarshalling: 然后,我有执行解组的类:

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

Now, depending on our context, I want the unmarshaller to use a specific implementation of Child for the Root object... but that's where I'm struggling: I have no idea how to signal the unmarshaller to use a specific subclass for Child (in our files, the structure of Root, Child and Other is always the same. However, how we process Child depends on the source folder for each file). 现在,根据我们的上下文,我希望解组器对Root对象使用Child的特定实现...但这就是我在努力的地方:我不知道如何发信号通知解组器对Child使用特定的子类(在我们的文件中,“根”,“子”和“其他”的结构始终相同,但是,如何处理“子”取决于每个文件的源文件夹。 I've tried passing the concrete class when creating the context -just for testing purposes- (eg JAXBContext.newInstance(Root.class, ChildImpl.class)), but for some reason, the unmarshaller always resolve to the class entered last in the @XmlElements array (In this case ChildImpl2). 我曾尝试在创建上下文时(仅出于测试目的)传递具体类(例如JAXBContext.newInstance(Root.class,ChildImpl.class)),但是由于某些原因,解组器始终解析为最后一个输入类@XmlElements数组(在本例中为ChildImpl2)。

I've also tried removing the @XmlElements annotation, but the unmarshaller doesn't know to process the Version elements since the parent class is abstract (that's also the reason why I added the @XmlTransient annotation; I have no interest in trying to instance AbstractChild) 我也尝试过删除@XmlElements注释,但是解组器不知道要处理Version元素,因为父类是抽象的(这也是我添加@XmlTransient注释的原因;我对尝试实例化没有兴趣AbstractChild)

Any ideas? 有任何想法吗? Thanks in advance! 提前致谢!

If the structure in the xml-files is always the same, having an abstract class AbstractChild makes no sense (in my opinion). 如果xml文件中的结构始终相同,那么在我看来,拥有抽象类AbstractChild毫无意义。 The logic should be elsewhere in your code, in a strategy or something like that. 逻辑应该在代码中的其他地方,策略中或类似的地方。

But it is kind of possible: 但这是可能的:

Have an XmlElement representing the child. 有一个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;
    }
}

Have another Class like MyAbstractChild 还有另一个像MyAbstractChild这样的类

public abstract class MyAbstractChild {
    private final Child child;

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

    public Child getChild() {
        return child;
    }
}

And subclasses for each behavior you want: 以及您想要的每种行为的子类:

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

    // custom behavior here
}

Then you can implement an XmlAdapter: 然后,您可以实现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();
    }
}

Now you can use the new type MyAbstractChild in your root element: 现在,您可以在根元素中使用新的类型MyAbstractChild:

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

Finally you can train the unmarshaller to use your adapter of choice: 最后,您可以训练解组器以使用您选择的适配器:

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);

Now you have your Root-Element with MyChildImpl-Objects in the children. 现在,您的子元素中具有带有MyChildImpl-Objects的Root元素。

But as I wrote in the beginning: there should be another option :) 但是正如我在开始时写道:应该有另一个选择:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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