Возможно ли, чтобы Jaxb демонтировал интерфейс? - PullRequest
1 голос
/ 28 сентября 2019

мой текущий код идеально подходит, и я получаю нужный элемент внутри полученного XML.то есть <food>Beef</food>

Однако проблема возникает, когда мне нужно отменить это обратно в Java-объект.Все возвращается нормально, кроме переменной питания.Изначально у меня не было XmlElement(required = true) сверху, и элемент питания всегда возвращался к нулю.Затем я добавил раздел 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}
}

Я просто хотел знать, если этоправильно, и нет действительно хорошего способа разобрать тип интерфейса.Это правда?Я видел много других вопросов о маршаллинговых интерфейсах, и неубедительные вопросы не получили ответов на мое удовлетворение.Любой ответ приветствуется, и я знаю, что это не минимальный воспроизводимый пример, но я больше ищу словесный ответ, а не исправление кода или что-то еще.Хотя, если в коде что-то явно не так, пожалуйста, дайте мне знать!

1 Ответ

1 голос
/ 29 сентября 2019

Для стандартных случаев JAXB может использовать только (абстрактные) классы, а не интерфейсы.

Опции, которые я могу придумать

  • Вы можете использовать интерфейсы с @XmlAdapter.См. Пример: [1]
  • Используйте Object для привязок JAXB и предоставьте интерфейс для приведения.(Может быть, добавить логику проверки в `afterUnmarshal (Unmarshaller u, Object parent). [2]
  • Привязать приватное поле к @XmlAnyElement и выполнить дальнейшую обработку в afterUnmarshal(Unmarshaller, Object), добавить @XmlTransient кtarget: см. пример: [3]

При некоторой креативности могут быть и другие варианты, но я думаю, что все сводится к основному: попробуйте перейти к «необработанным» параметрам разбора и заполните интерфейсссылка вручную.

[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
    }
}
...