Сериализатор MOXy JSON не может сериализовать класс прокси Java с интерфейсом, расширяющим другие суперинтерфейсы

Случай использования:

Мы разработали модулированный фреймворк на основе JOOQ, который является DSL для SQL. Каждый модуль будет иметь подмодуль API и подмодуль IMPL. На уровне API для каждого доступа ReadOnly мы предоставляем только определенный интерфейс "view", чтобы избежать путаницы, который содержит только геттеры.

Чтобы соединить модель и ReadOnly View, мы используем java Proxy.

Таким образом, был бы некоторый интерфейс высокого уровня, с которого может распространяться конкретный уровень. И тогда мы встретили ошибку:

Exception in thread "main" javax.xml.bind.JAXBException: 
Exception Description: The java interface example.json.demo2.IAddress can not be mapped by JAXB as    it has multiple mappable parent interfaces. Multiple inheritence is not supported
- with linked exception:
[Exception [EclipseLink-50089] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f):      org.eclipse.persistence.exceptions.JAXBException
Exception Description: The java interface example.json.demo2.IAddress can not be mapped by JAXB as it has multiple mappable parent interfaces. Multiple inheritence is not supported]
at org.eclipse.persistence.jaxb.JAXBContext$TypeMappingInfoInput.createContextState(JAXBContext.java:1108)
at org.eclipse.persistence.jaxb.JAXBContext.<init>(JAXBContext.java:188)
at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:165)
at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:152)
at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:112)
at org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(JAXBContextFactory.java:102)
at example.json.demo2.Demo.main(Demo.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: Exception [EclipseLink-50089] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.JAXBException
Exception Description: The java interface example.json.demo2.IAddress can not be mapped by JAXB as it has multiple mappable parent interfaces. Multiple inheritence is not supported
at org.eclipse.persistence.exceptions.JAXBException.invalidInterface(JAXBException.java:1116)
at org.eclipse.persistence.jaxb.javamodel.reflection.JavaClassImpl.getSuperclass(JavaClassImpl.java:360)
at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.postProcessXmlAccessorType(AnnotationsProcessor.java:1626)
at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.buildTypeInfo(AnnotationsProcessor.java:844)
at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.postBuildTypeInfo(AnnotationsProcessor.java:773)
at org.eclipse.persistence.jaxb.compiler.AnnotationsProcessor.processClassesAndProperties(AnnotationsProcessor.java:298)
at org.eclipse.persistence.jaxb.compiler.Generator.<init>(Generator.java:156)
at org.eclipse.persistence.jaxb.JAXBContext$TypeMappingInfoInput.createContextState(JAXBContext.java:1104)
... 11 more

Интерфейс ReadOnly View:

@XmlRootElement
public interface IAddress extends A, B {

@XmlPath("/address/@streetAddress")
String getStreet();

void setStreet(String street);

@XmlPath("/address/city/@cityName")
String getCity();

void setCity(String city);

@XmlPath("/address/state/@stateCode")
String getState();

void setState(String state);
@XmlPath("/address/country/@countryCode")
String getCountry();

void setCountry(String country);

@XmlPath("/address/@postalCode")
String getPostalCode();

void setPostalCode(String postalCode);
}

Два супер интерфейса: public interface A {}public interface B {}

Боб Класс:

@XmlType(propOrder = { "country", "state", "city", "street", "postalCode" })
public class Address implements IAddress {

@XmlPath("Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:Locality/ns:Thoroughfare/ns:ThoroughfareName/text()")
private String street;
@XmlPath("Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:Locality/ns:LocalityName/text()")
private String city;
@XmlPath("Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:AdministrativeAreaName/text()")
private String state;
@XmlPath("Placemark/ns:AddressDetails/ns:Country/ns:CountryNameCode/text()")
private String country;
@XmlPath("Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:Locality/ns:PostalCode/ns:PostalCodeNumber/text()")
private String postalCode;

@Override
public String getStreet() {
    return street;
}

@Override
public void setStreet(String street) {
    this.street = street;
}

@Override
public String getCity() {
    return city;
}

@Override
public void setCity(String city) {
    this.city = city;
}

@Override
public String getState() {
    return state;
}

@Override
public void setState(String state) {
    this.state = state;
}

@Override
public String getCountry() {
    return country;
}

@Override
public void setCountry(String country) {
    this.country = country;
}

@Override
public String getPostalCode() {
    return postalCode;
}

@Override
public void setPostalCode(String postalCode) {
    this.postalCode = postalCode;
}
}

Бегущий класс:

public class Demo {


public static void main(String[] args) throws Exception {
    final FileReader xmlURL = new FileReader("src/main/java/example/json/demo2/response.xml");
    final FileReader jsonURL = new FileReader("src/main/java/example/json/demo2/response.json");

    JAXBContext jc = JAXBContextFactory.createContext(new Class[]{Address.class, IAddress.class}, new HashMap());
    Unmarshaller unmarshaller = jc.createUnmarshaller();
    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    // XML
    XMLInputFactory xif = XMLInputFactory.newInstance();
    StreamSource xml = new StreamSource(xmlURL);
    XMLStreamReader xsr = xif.createXMLStreamReader(xml);
    xsr.nextTag(); // Advance to kml tag
    xsr.nextTag(); // Advance to Response tag
    JAXBElement<Address> addressFromXML = unmarshaller.unmarshal(xsr, Address.class);
    marshaller.marshal(addressFromXML, System.out);

    // JSON
    unmarshaller.setProperty("eclipselink.media-type", "application/json");
    unmarshaller.setProperty("eclipselink.json.include-root", false);
    StreamSource json = new StreamSource(jsonURL);

    final JAXBElement<Address> addressFromJSON = unmarshaller.unmarshal(json, Address.class);
    marshaller.setProperty("eclipselink.media-type", "application/json");
    marshaller.setProperty("eclipselink.json.include-root", false);
    final IAddress value = (IAddress) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{IAddress.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final Address address = addressFromJSON.getValue();
            return method.invoke(address, args);
        }
    });
    marshaller.marshal(value, System.out);
}
}

И кстати, если бы я прокомментировал это как:

public interface IAddress {//extends A, B {

Тогда я могу получить:

{
  "address" : {
  "postalCode" : "94043",
  "streetAddress" : "1600 Amphitheatre Pkwy",
  "city" : {
     "cityName" : "Mountain View"
  },
  "country" : {
     "countryCode" : "US"
  },
  "state" : {
     "stateCode" : "CA"
  }
}
}

Я пытался с MOXy 2.6.0-M3, и он все еще имеет ту же проблему.

1 ответ

Ниже приведен ответ, но, похоже, некоторые проблемы мешают его работе:

Вариант № 1 - Изменить Супер Тип IAddress

Вы можете использовать внешний картографический документ MOXy, чтобы указать, что супер тип IAddress является java.lang.Objectили только один из A или же B, вместо A а также B,

<?xml version="1.0"?>
<xml-bindings 
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="example.json.demo2">
    <java-types>
        <java-type name="IAddress" super-type="java.lang.Object"/>
    </java-types>
</xml-bindings>

Это переопределение работает для классов, но MOXy, кажется, имеет проблему, когда это используется на интерфейсах, см. Следующую проблему:

Вариант № 2 - Удалить A & B из наследственной иерархии

В качестве альтернативы вы можете использовать xml-transient удалять A а также B из иерархии наследования (см.: http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html).

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="example.json.demo2">
    <java-types>
        <java-type name="A" xml-transient="true"/>
        <java-type name="B" xml-transient="true"/>
    </java-types>
</xml-bindings>

Это переопределение работает для классов, но MOXy, кажется, имеет проблему, когда это используется на интерфейсах, см. Следующую проблему:

Применение внешних метаданных

Файл сопоставления применяется при создании JAXBContext следующее:

Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "example/binding.xml");
JAXBContext jc = JAXBContext.newInstance("example", Customer.class.getClassLoader() , properties);
Другие вопросы по тегам