Сериализатор 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);