@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 年前就确认了这个问题,但似乎他们还没有解决,我也遇到过类似的问题。

基本上,我们可以使用 beforeMarshalafterUnmarshal 方法来更改字段中的值。

  1. 您需要使用 @XmlAnyElement(lax=true)Map<String,Object> 创建字段 List<Object>
  2. 删除 @XmlPath(".")XMLAdapter class。
  3. 将字段 Map<String, Object> 标记为 @XmlTransient

现在可以在 beforeMarshalafterMarshal 字段中交换数据。在 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的创建:

你可以在这里参考我的完整答案: