3

having this Jaxb Xml definition, i try to remove the Map Elements Wrapper by adding @XmlPath(".") but it cause exception during the unmarchaling

@XmlRootElement
public abstract class ViewElement{
    @XmlJavaTypeAdapter(value=EventAdapter.class)   
    public Map<Event, String> getEvents() {     
    }
    private transient Class entityType;
    public Class getEntityType() {
        return entityType;
    }
}

And the EventAdapter is

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

my output was

<element>
   <events>
      <onCellEdit>do some thing<onCellEdit>
   </events>
   <entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>

I try to remove the <events> tag by adding @XmlPath(".")

@XmlPath(".")
@XmlJavaTypeAdapter(value=EventAdapter.class)   
public Map<Event, String> getEvents() {     
}

The output is good

<element>
   <onCellEdit>do some thing<onCellEdit>       
   <entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>

but the unmarchaling faileds

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)

Debuging Jaxb bring me to the line

org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue

public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
    ...
    line 205 unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}

During the unmarchaling of entityType value, the UnmarshalRecordImpl.currentObj contains the EventAdapter instead of the parent element

I modify 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
    }
....
}

Not a safe solution

Nassim MOUALEK
  • 4,702
  • 4
  • 25
  • 44
  • Hello, Thanks a lot for this question. Actually even I am stuck in this issue for a long time now. After a lot of search and trial and error methods, I was able to find this question. Have you been able to find the workaround for this? Because event with version `3.0.0` of `Moxy` I am still finding the issue. Also, the `Status` of the bug ticket still shows as `NEW`. so was wondering what happened. I have posted my issue here: https://stackoverflow.com/q/67648941/7584240 – BATMAN_2008 Jun 04 '21 at 11:07
  • Hi I tried the solution mentioned on the ticket. But still it did not work for me. I have posted my question here can you please have a look and provide your solution: https://stackoverflow.com/q/67860792/7584240 – BATMAN_2008 Jun 07 '21 at 14:22
  • Hi there, it's have been a while, but indeed I remember that I didn't find a perfect solution for this problem – Nassim MOUALEK Jun 07 '21 at 16:10
  • Thanks for your response mate. Ya, it's been a long time but if possible can you please have a look at this issue and see if you can provide some workaround as you did for yours or some suggestion? Even a minute a suggestion or workaround would be a great help for me as its been many days trying to figure out this: https://stackoverflow.com/q/67860792/7584240 – BATMAN_2008 Jun 07 '21 at 16:14
  • @BATMAN_2008 sorry it's very far for me :) – Nassim MOUALEK Jun 11 '21 at 07:51
  • Thanks for your response. Np:) I found the solution myself after a lot of research and efforts. I have posted it here if in case you need it for reference: https://stackoverflow.com/a/67923216/7584240 – BATMAN_2008 Jun 11 '21 at 07:57
  • @BATMAN_2008 may be you should send Merge request if your solution is generic enougth – Nassim MOUALEK Jun 11 '21 at 09:37
  • Thanks for the info. Actually, I have not made any changes to the `EclipseLink/Moxy` code rather I have written additional code within my application to handle this condition. However, I believe there seems to be an issue with the `EclipseLink Moxy` code which is pulling everything into the same field when annotated with `@XmlPath(".")`. I have posted this issue on their `Github` but seems like they do not respond much as it's been nearly 10 days. Moreover, your issue is around 6 years old but still, the issue exists. But luckily there is a generic workaround as of now if people are stuck. – BATMAN_2008 Jun 11 '21 at 09:42
  • 1
    I tried to debug the `EclipseLink Moxy` code but it's so huge and complex. With my knowledge of Java it's very difficult to pinpoint what might be wrong as so many things are going on there. – BATMAN_2008 Jun 11 '21 at 09:43
  • I a very good in Java and I didn't succeed to fix this problem – Nassim MOUALEK Jun 11 '21 at 09:48

2 Answers2

1

I have been able to reproduce the issue that you are seeing, but haven't yet worked out the cause. You can use the following bug to track the progress on this issue:

bdoughan
  • 147,609
  • 23
  • 300
  • 400
0

After trying a lot of things, I was able to find a workaround for this issue. I thought of posting here the same so it can be helpful to someone else in the future. The lead has confirmed the issue around 5 years ago but seems like they have not fixed it and I was facing a similar issue.

Basically, we can use the beforeMarshal and afterUnmarshal methods to change the values in the fields.

  1. You need to create a field List<Object> with @XmlAnyElement(lax=true) along with Map<String,Object>.
  2. Remove the @XmlPath(".") and the XMLAdapter class.
  3. Mark the field Map<String, Object> with @XmlTransient.

Now within the beforeMarshal and afterMarshal fields, you can exchange the data. During the unmarshal in beforeunmarshal, all the unknown field values will be present within the List<Object> loop over it and add it to the Map<String, Object>.

Similarly during the marshaling, you can move the values Map<String, Object> to List<Object> by creating the DOM elements.

Marshaling all values are added to root as DOM Elements are present and during Unmarshaling known values are read first and then-unknown values are stored within List<Object> due to @XmlAnyElement.

I have created an example using the Customer class, you can modify it accordingly for your need.

@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();
    }
}

You can refer the creation of DOM ELEMENTS here:https://stackoverflow.com/a/24239105/7584240

You can refer my complete answer here: https://stackoverflow.com/a/67923216/7584240

BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98