Jaxb 是否可以解组到接口?

Is it possible for Jaxb to unmarshall to an interface?

我当前的代码编组非常完美,我在结果 XML 中得到了我想要的元素。即 <food>Beef</food>

但是,当我必须将其解组回 java 对象时,问题就来了。一切 returns 都很好,除了食物变量。我最初没有 XmlElement(required = true) 在上面,食物元素总是解组回 null。然后,我添加了 required=true 部分,但我遇到了界面问题。我做了一些挖掘,从我能收集到的信息来看,jaxb 无法真正解组到接口中,因为它不知道要编组到的具体类型。

当前错误如果这有帮助:

Can not set FoodInterface field BigPayload.food to 
com.sun.org.apache.xerces.internal.dom.ElementNSImpl

我的Java类如下:

@XmlSeeAlso({MeatFoods.class, VeggieFoods.class})
@XmlType(name ="BigPayload", propOrder = //stuff goes here
@XmlRootElement(name = foodPayload)
public class BigPayload implements Payload{
    @XmlElements({@XmlElement(type = MeatFoods.class), 
                  @XmlElement(type = VeggieFoods.class),
                  @XmlElement(required = true)})
    protected FoodInterface food;
    protected Grade grade;
    //grade/food setters and getters
}
@XmlTransient //If this isn't here, I get the jaxB cannot handle interfaces and no default constructor error
public interface FoodInterface{ //stuff here}
@XmlType(name = "MeatFoods")
@XmlEnum
public enum MeatFoods implements FoodInterface{
    Chicken(1, Chicken)
    Beef(2, Beef)
    Pork(3, Pork)

    int value;
    String name;

    @Override
    public int getValue()

    @Override
    public String getName()

    public static FoodInterface getEnumFromValue(int value){//gets stuff}
    public static FoodInterface getEnumFromName(String name){//gets stuff}
}

我只是想知道这是否正确,没有真正好的方法来解组接口类型。这是真的?我看到很多其他问题都是关于编组接口的,而解组问题并没有真正得到令我满意的答案。任何答案表示赞赏,我知道这不是一个最小的可重现示例,但我更想寻找口头答案而不是代码修复或任何东西。不过,如果代码中有任何明显错误,请告诉我!

对于标准情况,JAXB 只能使用(抽象)类 而不是接口。

我能想到的选项

  • 您可以使用 @XmlAdapter 的接口。参见示例:[1]
  • 对 JAXB 绑定使用 Object 并通过转换公开接口。 (也许将验证逻辑添加到 `afterUnmarshal(Unmarshaller u, Object parent) 中。[2]
  • @XmlAnyElement绑定一个私有字段,在afterUnmarshal(Unmarshaller, Object)做一些进一步的处理,给目标添加@XmlTransient。参见示例:[3]

如果有一些创意,可能会有一些其他的选择。但我认为一切都归结为基本:尝试进入 "raw" 解析选项并手动填写接口参考。

[1]

public static interface Food {
    String name();
}
public enum Veggie implements Food {
    SALAD;
}
public static enum Meat implements Food {
    CHICKEN;
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlJavaTypeAdapter(FoodAdapter.class)
    @XmlAttribute
    private Food food;

    public Food getFood() {
        return food;
    }

    public void setFood(Food food) {
        this.food = food;
    }
}

public static class FoodAdapter extends XmlAdapter<String, Food> {

    @Override
    public Food unmarshal(String v) throws Exception {
        try {
            return Veggie.valueOf(v);
        } catch (IllegalArgumentException e) {

        }
        try {
            return Meat.valueOf(v);
        } catch (IllegalArgumentException e) {

        }
        throw new IllegalArgumentException("Unknown Food:" + v);
    }

    @Override
    public String marshal(Food v) throws Exception {
        return v.name();
    }

}

[2]

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlElement
    private Object food;

    public Food getFood() {
        return (Food) food;
    }

    public void setFood(Food food) {
        this.food = food;
    }

    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        if (food != null && !(food instanceof Food)) {
            throw new IllegalArgumentException("food is of wrong type: " + food.getClass().getName());
        }
    }
}

JAXBContext newInstance = JAXBContext.newInstance(UseInterface.class, Meat.class, Veggie.class);
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><useInterface><food xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"meat\">CHICKEN</food></useInterface>";

newInstance.createUnmarshaller().unmarshal(new StringReader(xml));

[3]

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlAnyElement
    private org.w3c.dom.Element foo;

    @XmlTransient
    private SomeInterface ifc


    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        NamedNodeMap attributes = foo.getAttributes();
        // do something with foo on DOM level to bind the subtree to an interface manually
    }
}