1

I have a bean type that needs to be marshalled to and unmarshalled from JSON using the EclipseLink JAXB implementation. When the bean includes a field of type java.util.Map, the unmarshalling fails with:

Exception in thread "main" javax.xml.bind.UnmarshalException
 - with linked exception:
[Exception [EclipseLink-25023] (Eclipse Persistence Services - 2.6.7.v20190604-418f1a1c56): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to attribute value.]
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1110)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:172)
    at org.example.jaxb.ExampleBean.main(ExampleBean.java:43)
Caused by: Exception [EclipseLink-25023] (Eclipse Persistence Services - 2.6.7.v20190604-418f1a1c56): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to attribute value.
    at org.eclipse.persistence.exceptions.XMLMarshalException.noDescriptorFound(XMLMarshalException.java:284)
    at org.eclipse.persistence.internal.oxm.record.deferred.DescriptorNotFoundContentHandler.processComplexElement(DescriptorNotFoundContentHandler.java:35)
    at org.eclipse.persistence.internal.oxm.record.deferred.DeferredContentHandler.startElement(DeferredContentHandler.java:94)

I have written a short demonstration of the problem in which I use JAXB to write out a bean and then immediately read back what was produced. It amazes me that JAXB cannot parse its own output! Full code is below:

package org.example.jaxb;

import java.io.*;
import java.util.*;

import javax.xml.bind.*;
import javax.xml.bind.annotation.*;

@XmlRootElement(name = "ExampleBean")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExampleBean {

  @XmlElement
  private Map<String, Object> properties;

  public synchronized Object getProperty(String key) {
    return properties != null ? properties.get(key) : null;
  }
  public synchronized void setProperty(String key, Object value) {
    if (properties == null) {
      properties = new HashMap<>();
    }
    properties.put(key, value);
  }

  public static void main(String[] args) throws Exception {
    ExampleBean initialBean = new ExampleBean();
    initialBean.setProperty("Foo", "Bar");

    JAXBContext context = JAXBContext.newInstance(ExampleBean.class);
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty("eclipselink.media-type", "application/json");
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    marshaller.marshal(initialBean, buffer);

    System.out.println(buffer.toString());

    Unmarshaller unmarshaller = context.createUnmarshaller();
    unmarshaller.setProperty("eclipselink.media-type", "application/json");
    ExampleBean loadedBean = (ExampleBean) unmarshaller.unmarshal(new ByteArrayInputStream(buffer.toByteArray()));
    System.out.printf("Read back ExampleBean with property %s = %s%n", "Foo", loadedBean.getProperty("Foo"));  }
}

The full output is as follows:

{
   "ExampleBean" : {
      "properties" : {
         "entry" : [ {
            "key" : "Foo",
            "value" : {
               "type" : "string",
               "value" : "Bar"
            }
         } ]
      }
   }
}
Exception in thread "main" javax.xml.bind.UnmarshalException
 - with linked exception:
[Exception [EclipseLink-25023] (Eclipse Persistence Services - 2.6.7.v20190604-418f1a1c56): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to attribute value.]
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1110)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:172)
    at org.example.jaxb.ExampleBean.main(ExampleBean.java:43)
Caused by: Exception [EclipseLink-25023] (Eclipse Persistence Services - 2.6.7.v20190604-418f1a1c56): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to attribute value.
    at org.eclipse.persistence.exceptions.XMLMarshalException.noDescriptorFound(XMLMarshalException.java:284)
    at org.eclipse.persistence.internal.oxm.record.deferred.DescriptorNotFoundContentHandler.processComplexElement(DescriptorNotFoundContentHandler.java:35)
    at org.eclipse.persistence.internal.oxm.record.deferred.DeferredContentHandler.startElement(DeferredContentHandler.java:94)
    [... stack trace abbreviated]

What am I doing wrong? How can I parse this JSON data to a Map?

Note that I cannot use a solution that changes the shape of JSON output for the map entries, since I need to consume existing files with that shape.

Relevant versions as follows:

  • JDK 1.8.0_181
  • org.eclipse.persistence.moxy/core/asm 2.6.7 (from Maven Central).

Update

At the suggestion of @MilenDyankov I changed the map type to Map<String,String>. Interestingly this does now work, though unfortunately it doesn't help me because I need to read generic data into the map from existing files. When the map value is String, the value node of the entry is a plain text field rather than a nested node with type and value field.

Another interesting observation is that the marshal/unmarshal (with the original Map<String,Object>) work together fine for XML formatted data but not JSON.

Neil Bartlett
  • 23,743
  • 4
  • 44
  • 77
  • Does it work if you change the map's value type to something other than `Object`? I don't know about your particular case but in my experience many of the serialization libs don't handle well (particularly during deserialization) collections of generic types. – Milen Dyankov Aug 21 '19 at 14:54
  • @MilenDyankov Interesting, yes it does work if I change to `Map`. The output from the marshalling call is no longer a nested object with the `type` and `value` fields. Unfortunately this doesn't help me because the files I need to read have generic data in the original format described. Anyway I will add this information to the question. Thank you! – Neil Bartlett Aug 21 '19 at 14:59
  • One workaround I've used with other library was to have `MyOwnType` that extends the generic collection. Not sure if it will work here but I would try something like `public interface MyOwnType extends Map ...` and see if using that type instead of `Map` works. – Milen Dyankov Aug 21 '19 at 15:06
  • @MilenDyankov I have found a simpler workaround: version 2.5.x of Moxy works fine! Seems something broke in 2.6+. – Neil Bartlett Aug 21 '19 at 15:13
  • @MilenDyankov I am also trying something similar. Actually, if you put `@XmlPath(".")` then it would work but it's resulting in some other issue. I have tried many things but unable to resolve it. If possible please have a look at this question where I have provided complete code sample and explanation and provide your suggestion to resolve the issue: https://stackoverflow.com/questions/67648941/jaxb-moxy-unmarshalling-assigns-all-field-values-to-mapstring-object-rather-th – BATMAN_2008 May 23 '21 at 11:49
  • If you try with `Map` with `@XmlPath(".")` then it would work but results in one more issue posted here: If you have some suggestion then please provide the same for this issue: https://stackoverflow.com/questions/67648941/jaxb-moxy-unmarshalling-assigns-all-field-values-to-mapstring-object-rather-th – BATMAN_2008 May 23 '21 at 11:51

0 Answers0