@XmlPath(".") 与@XmlAdapter 冲突
@XmlPath(".") conflicts with @XmlAdapter
有了这个 Jaxb Xml 定义,我尝试通过添加 @XmlPath(".")
来删除 Map Elements Wrapper,但是它在解组期间导致异常
@XmlRootElement
public abstract class ViewElement{
@XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
private transient Class entityType;
public Class getEntityType() {
return entityType;
}
}
事件适配器是
public class EventAdapter extends XmlAdapter<EventAdapter.AdaptedMap, Map<Event, String>> {
public static class AdaptedMap {
@XmlVariableNode("key")
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
public static class AdaptedEntry {
@XmlTransient
public String key;
@XmlValue
public String value;
}
.....
}
我的输出是
<element>
<events>
<onCellEdit>do some thing<onCellEdit>
</events>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
我尝试通过添加 @XmlPath(".")
来删除 <events>
标签
@XmlPath(".")
@XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
输出不错
<element>
<onCellEdit>do some thing<onCellEdit>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
但取消编组失败
Caused by: Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[entityType-->view.entityType/text()]] with descriptor [XMLDescriptor(com.agitech.erp.view.BeanView --> [DatabaseTable(view), DatabaseTable(viewFrame), DatabaseTable(viewElement)])], could not be converted to [class java.lang.Class].
Internal Exception: java.lang.ClassNotFoundException:
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConvertedToClass(ConversionException.java:95)
at org.eclipse.persistence.internal.helper.ConversionManager.convertObjectToClass(ConversionManager.java:446)
调试 Jaxb 让我上线
org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
...
line 205 unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}
在解组 entityType 值期间,UnmarshalRecordImpl.currentObj
包含 EventAdapter 而不是父元素
我修改org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
....
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();
// by default it return the EventAdapter, changing it to NULL fix my problem
}
....
}
不是安全的解决方案
我已经能够重现您遇到的问题,但尚未找出原因。您可以使用以下错误来跟踪此问题的进展:
在尝试了很多事情之后,我找到了解决这个问题的方法。我想在这里发布相同的内容,以便将来对其他人有所帮助。领导在大约 5 年前就确认了这个问题,但似乎他们还没有解决,我也遇到过类似的问题。
基本上,我们可以使用 beforeMarshal
和 afterUnmarshal
方法来更改字段中的值。
- 您需要使用
@XmlAnyElement(lax=true)
和 Map<String,Object>
创建字段 List<Object>
。
- 删除
@XmlPath(".")
和 XMLAdapter
class。
- 将字段
Map<String, Object>
标记为 @XmlTransient
。
现在可以在 beforeMarshal
和 afterMarshal
字段中交换数据。在 beforeunmarshal,
中的 unmarshal
期间,所有未知字段值将出现在 List<Object>
循环中,并将其添加到 Map<String, Object>
.
与 marshaling
期间类似,您可以通过创建 DOM
元素将值 Map<String, Object>
移动到 List<Object>
。
Marshaling
所有值都添加到 root
,因为存在 DOM Elements
,并且在 Unmarshaling
期间首先读取已知值,然后将未知值存储在 List<Object>
由于 @XmlAnyElement
.
我已经使用 Customer
class 创建了一个示例,您可以根据需要进行相应的修改。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@AllArgsConstructor
@ToString
@NoArgsConstructor
public class Customer {
@XmlTransient
private String isA;
private String name;
private String age;
@XmlAnyElement(lax = true)
@JsonIgnore
private List<Object> otherElements = new ArrayList<>();
@JsonIgnore
@XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
@JsonAnyGetter
@JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
你可以在这里参考DOM ELEMENTS
的创建:
你可以在这里参考我的完整答案:
有了这个 Jaxb Xml 定义,我尝试通过添加 @XmlPath(".")
来删除 Map Elements Wrapper,但是它在解组期间导致异常
@XmlRootElement
public abstract class ViewElement{
@XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
private transient Class entityType;
public Class getEntityType() {
return entityType;
}
}
事件适配器是
public class EventAdapter extends XmlAdapter<EventAdapter.AdaptedMap, Map<Event, String>> {
public static class AdaptedMap {
@XmlVariableNode("key")
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
public static class AdaptedEntry {
@XmlTransient
public String key;
@XmlValue
public String value;
}
.....
}
我的输出是
<element>
<events>
<onCellEdit>do some thing<onCellEdit>
</events>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
我尝试通过添加 @XmlPath(".")
<events>
标签
@XmlPath(".")
@XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
输出不错
<element>
<onCellEdit>do some thing<onCellEdit>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
但取消编组失败
Caused by: Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[entityType-->view.entityType/text()]] with descriptor [XMLDescriptor(com.agitech.erp.view.BeanView --> [DatabaseTable(view), DatabaseTable(viewFrame), DatabaseTable(viewElement)])], could not be converted to [class java.lang.Class].
Internal Exception: java.lang.ClassNotFoundException:
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConvertedToClass(ConversionException.java:95)
at org.eclipse.persistence.internal.helper.ConversionManager.convertObjectToClass(ConversionManager.java:446)
调试 Jaxb 让我上线
org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
...
line 205 unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}
在解组 entityType 值期间,UnmarshalRecordImpl.currentObj
包含 EventAdapter 而不是父元素
我修改org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
....
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();
// by default it return the EventAdapter, changing it to NULL fix my problem
}
....
}
不是安全的解决方案
我已经能够重现您遇到的问题,但尚未找出原因。您可以使用以下错误来跟踪此问题的进展:
在尝试了很多事情之后,我找到了解决这个问题的方法。我想在这里发布相同的内容,以便将来对其他人有所帮助。领导在大约 5 年前就确认了这个问题,但似乎他们还没有解决,我也遇到过类似的问题。
基本上,我们可以使用 beforeMarshal
和 afterUnmarshal
方法来更改字段中的值。
- 您需要使用
@XmlAnyElement(lax=true)
和Map<String,Object>
创建字段List<Object>
。 - 删除
@XmlPath(".")
和XMLAdapter
class。 - 将字段
Map<String, Object>
标记为@XmlTransient
。
现在可以在 beforeMarshal
和 afterMarshal
字段中交换数据。在 beforeunmarshal,
中的 unmarshal
期间,所有未知字段值将出现在 List<Object>
循环中,并将其添加到 Map<String, Object>
.
与 marshaling
期间类似,您可以通过创建 DOM
元素将值 Map<String, Object>
移动到 List<Object>
。
Marshaling
所有值都添加到 root
,因为存在 DOM Elements
,并且在 Unmarshaling
期间首先读取已知值,然后将未知值存储在 List<Object>
由于 @XmlAnyElement
.
我已经使用 Customer
class 创建了一个示例,您可以根据需要进行相应的修改。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@AllArgsConstructor
@ToString
@NoArgsConstructor
public class Customer {
@XmlTransient
private String isA;
private String name;
private String age;
@XmlAnyElement(lax = true)
@JsonIgnore
private List<Object> otherElements = new ArrayList<>();
@JsonIgnore
@XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
@JsonAnyGetter
@JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
@JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
你可以在这里参考DOM ELEMENTS
的创建:
你可以在这里参考我的完整答案: