Coder Social home page Coder Social logo

fasterxml / jackson-module-jaxb-annotations Goto Github PK

View Code? Open in Web Editor NEW
44.0 13.0 22.0 832 KB

(DEPRECATED!!!) Moved to: https://github.com/FasterXML/jackson-modules-base/

Home Page: https://github.com/FasterXML/jackson-modules-base

Shell 0.02% Java 99.98%

jackson-module-jaxb-annotations's Introduction

NOTE: This module has become part of Jackson Base Modules repo. as of Jackson 2.9

This repo still exists to allow release of patch versions of older versions; it will be hidden (made private) in near future.


Overview

This Jackson extension module provides support for using JAXB (javax.xml.bind) annotations as an alternative to native Jackson annotations. It is most often used to make it easier to reuse existing data beans that used with JAXB framework to read and write XML.

Maven dependency

To use this extension on Maven-based projects, use following dependency:

<dependency>
  <groupId>com.fasterxml.jackson.module</groupId>
  <artifactId>jackson-module-jaxb-annotations</artifactId>
  <version>2.4.0</version>
</dependency>

(or whatever version is most up-to-date at the moment)

Usage

To enable use of JAXB annotations, one must add JaxbAnnotationIntrospector provided by this module. There are two ways to do this:

  • Register JaxbAnnotationModule, or
  • Directly add JaxbAnnotationIntrospector

Module registration works in standard way:

JaxbAnnotationModule module = new JaxbAnnotationModule();
// configure as necessary
objectMapper.registerModule(module);

and the alternative -- explicit configuration is done as:

AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
// if ONLY using JAXB annotations:
mapper.setAnnotationIntrospector(introspector);
// if using BOTH JAXB annotations AND Jackson annotations:
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
mapper.setAnnotationIntrospector(new AnnotationIntrospector.Pair(introspector, secondary);

Note that by default Module version will use JAXB annotations as the primary, and Jackson annotations as secondary source; but you can change this behavior

jackson-module-jaxb-annotations's People

Contributors

arturdryomov avatar cowtowncoder avatar prb avatar q3aiml avatar simonetripodi avatar splatch avatar stolyarchukav avatar tatu-at-salesforce avatar valery1707 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jackson-module-jaxb-annotations's Issues

Jackson can't handle @XmlElements and @XmlElementWrapper correctly

Code written below works perfectly with plain JAXB but causes issues with Jackson. I found no way to handle similar structure by Fasterxml - wrapper + child elements having various names.

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

    @XmlElementWrapper(name = "items")
    @XmlElements({
        @XmlElement(name = "double", type = Double.class),
        @XmlElement(name = "integer", type = Integer.class)
    })
    private List<Number> items;

    public static void main(String[] args) throws Exception {
        JAXBContext context = JAXBContext.newInstance(XmlFail.class);

        String xml = 
        "<foo>" +
            "<items>" +
                "<double>2.0</double>" +
                "<integer>1</integer>" +
            "</items>" +
        "</foo>";

        XmlFail unmarshal = (XmlFail) context.createUnmarshaller().unmarshal(new ByteArrayInputStream(xml.getBytes()));
        System.out.println(unmarshal.getClass() + " " + unmarshal.items);
    }

}```

XmlIDREF not being honored, causes infinite recursion

I have a simple Company/Employee example where the Employee class looks like this:

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {

  @XmlAttribute
  @XmlID
  private String id;

  @XmlAttribute
  private String name;

  @XmlIDREF
  private Employee manager;

  @XmlElement(name="report")
  @XmlIDREF
  private List<Employee> reports;

  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Employee getManager() {
    return manager;
  }
  public void setManager(Employee manager) {
    this.manager = manager;
  }
  public List<Employee> getReports() {
    return reports;
  }
  public void setReports(List<Employee> reports) {
    this.reports = reports;
  }
  public Employee() {
    reports = new ArrayList<Employee>();
  }
} 

When using JAXB to write the data, I get what I expect:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
    <employee id="1" name="Jane Doe">
        <report>2</report>
        <report>3</report>
    </employee>
    <employee id="2" name="John Smith">
        <manager>1</manager>
    </employee>
    <employee id="3" name="Anne Jones">
        <manager>1</manager>
    </employee>
</company>

When using Jackson, I get an infinite loop that starts out like this:

<?xml version='1.1' encoding='UTF-8'?><Company><employees>
    <employees>
      <id>1</id>
      <name>Jane Doe</name>
      <reports>
        <reports>
          <id>2</id>
          <name>John Smith</name>
          <manager>
            <id>1</id>
            <name>Jane Doe</name>
            <reports>
              <reports>
                <id>2</id>
                <name>John Smith</name>
                <manager>
                  <id>1</id>
                  <name>Jane Doe</name>
                  <reports>
                    <reports>
                      <id>2</id>
                      <name>John Smith</name>
                      <manager>
                        <id>1</id>
                        <name>Jane Doe</name>
                        <reports>
                          <reports>
                            <id>2</id>
                            <name>John Smith</name>
                            <manager>
                              <id>1</id>
                              <name>Jane Doe</name>
                              <reports>
                                <reports>
                                  <id>2</id>
                                  <name>John Smith</name>
                                  <manager>
                                    <id>1</id>
                                    <name>Jane Doe</name>
                                    <reports>
                                      <reports>
                                        <id>2</id>
                                        <name>John Smith</name>
                                        <manager>
...

I am using Jackson 2.4.3

if @XmlID and @XmlIDREF don't exist in the same object,will generate strange result

I test @xmlid and @XmlIDREF ,but I got some strange result.
My model look like:
Employee n-1 Department
Employee object use @XmlIDREF for Department object,it looks like:
@XmlIDREF
protected Department department;

and then Department using @xmlid ,looks like:
@xmlid
@XmlJavaTypeAdapter(value = LongAdapter.class, type = String.class)
protected Long id;

and in Deparment I didn't use @XmlIDREF ,just like :
protected List employees = new ArrayList();

OK,I set test Employee id 11,22,33 and both belong to Department with id 9
The result is very Strange:
[{"id":11,"name":"11","email":"[email protected]","department":{"id":9,"name":"department9","employees":[{"id":11,"name":"11","email":"[email protected]","department":9},{"id":22,"name":"22","email":"[email protected]","department":9},{"id":33,"name":"33","email":"[email protected]","department":9}]}},{"id":22,"name":"22","email":"[email protected]","department":9},{"id":33,"name":"33","email":"[email protected]","department":9}]

the first Employee will just show the full department info(wrong) and in Depatment object all Employee objecs are good then the follow two Employee are OK too.

My guess is only in the class with @xmlid will do the right trick and then the result will cache.
And I did research the JaxbAnnotationIntrospector.java but didn't understand it,but I only see xmlid.class is used for these two annotation add.
Is it a good guess?

XmlElementWrapper being ignored in Jackson 2.1 and 2.2

If you annotated a method with XmlElementWrapper, it used to be respected in Jackson 2.0.x. On 2.1 and 2.2 it's broken. (Note this is different from #13 since there is no XmlElement annotation involved.)

The change was added by commit 0a522e2. It's not clear from the message and the comments what is that trying to fix.

It would be nice if the previous behavior was maintained, even though 2.1. and 2.2 are already out of the door...

Infinite recursion error when using JaxbAnnotationIntrospector with JavaTypeXmlAdapter that does not change entity class.

As in the title.
If I use JaxbAnnotationIntrospector with entity (model posted in JUnit), that uses XmlJavaTypeAdapter that does not change instance class, i get:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: test.jackson.Entity["relatedEntities"]->test.jackson.RelatedEntities["relatedEntity"])
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:518)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:117)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:464)
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:504)
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:117)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:108)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2407)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:1953)
    at test.jackson.JacksonCyclicReferenceTest.testJacksonAdapters(JacksonCyclicReferenceTest.java:52)

I think it may be related to already closed issue: https://jira.codehaus.org/browse/JACKSON-508
Here is the JUnit that proves it works with JAXB, but does not with Jackson (tested with all available 2.0.x versions).

package test.jackson;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.junit.Test;

import test.jackson.JacksonCyclicReferenceTest.Entity.RelatedEntities;
import test.jackson.JacksonCyclicReferenceTest.Entity.RelatedEntities.RelatedEntity;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

public class JacksonCyclicReferenceTest {

    @Test
    /**
     * JAXB entity and adapter test. Just to prove that JAXB can handle such adapters.
     * @throws Exception
     */
    public void testJaxbEntity() throws Exception {
        JAXBContext context = JAXBContext.newInstance(Entity.class);
        Marshaller marshaller = context.createMarshaller();

        Entity entity = createSimpleEntity();
        marshaller.marshal(entity, System.out);

        entity = createEntityWithRelation();
        marshaller.marshal(entity, System.out);
    }

    @Test
    public void testJacksonAdapters() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());

        Entity entity = createSimpleEntity();
        mapper.writeValue(System.out, entity);

        entity = createEntityWithRelation();
        mapper.writeValue(System.out, entity);
    }

    private Entity createSimpleEntity() {
        Entity entity = new Entity();
        entity.id = 1L;
        return entity;
    }

    private Entity createEntityWithRelation() {
        Entity entity = new Entity();
        entity.id = 1L;
        entity.relatedEntities = new RelatedEntities();
        entity.relatedEntities.relatedEntity = new ArrayList<RelatedEntity>();

        Entity relatedEntity = new Entity();
        entity.id = 2L;
        RelatedEntity re = new RelatedEntity();
        re.entity = relatedEntity;
        entity.relatedEntities.relatedEntity.add(re);

        return entity;
    }

    @XmlAccessorType( XmlAccessType.FIELD )
    @XmlType( name = "", propOrder = { "relatedEntities" } )
    @XmlRootElement( name = "entity" )
    public static class Entity {

        @XmlAttribute
        Long id;

        RelatedEntities relatedEntities;

        @XmlAccessorType( XmlAccessType.FIELD )
        @XmlType( name = "", propOrder = { "relatedEntity" } )
        public static class RelatedEntities {

            List<Entity.RelatedEntities.RelatedEntity> relatedEntity;

            @XmlAccessorType( XmlAccessType.FIELD )
            @XmlType( name = "", propOrder = { "entity" } )
            @XmlJavaTypeAdapter( CycleRemovingAdapter.class )
            public static class RelatedEntity {

                @XmlElement( required = true )
                Entity entity;
            }
        }
    }

    public static class CycleRemovingAdapter extends XmlAdapter<RelatedEntity, RelatedEntity> {

        /** {@inheritDoc} */
        @Override
        public RelatedEntity unmarshal(RelatedEntity v) throws Exception {
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public RelatedEntity marshal(RelatedEntity v) throws Exception {
            // Only entity id will be left filled.
            v.entity.relatedEntities = null;
            return v;
        }

    }

}

@XmlElementWrapper and MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME

I have an object that has three List containers. They are marked up with @XmlElementWrapper annotations. I have added the configuration to include the wrapper name as the property (see first code block below). After digging through source, it's clear to me that jackson-module-jaxb-annotations is not aware of these properties.

That is fine, however in this case you cannot have multiple containers like this in a row. This module looks at the @xmlelement name instead of the wrapper, and the field list then creates one entry for all 3 of the below list containers since they contain the same abstract class. When POJOPropertitesCollector in the jackson-databind goes to rename the properties (as a result of having USE_WRAPPER_NAME_AS_PROPERTY_NAME enabled) it comes upon this entry and can't figure out what's going on. You get a JSONException with the message "Multiple value properties defined (xxx,xxx...".

There's explicit code commented out for this. I don't know where this responsibility lies, but I was able to fix this by just uncommenting the code, and moving the block about the wrapper above the @xmlelement block (as that is my preference). Would be nice if there were flags for the different ways this can be done. Also, that method is static, which makes it pretty hard to override to handle how a particular individual might want to.

 ObjectMapper jacksonObjectMapper = new ObjectMapper();
        jacksonObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        jacksonObjectMapper.enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME);
        jacksonObjectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

Annotated Pojo

    @XmlElementWrapper(name = "topics", required = true, namespace = "http://optimus.com/view/topic")
    @XmlElement(name = "export", namespace = "http://company.com/view/topic")
    protected List<TopicSearchExportView> topics;
    @XmlElementWrapper(name = "partners", required = true, namespace = "http://optimus.com/view/partner")
    @XmlElement(name = "export", namespace = "http://company.com/view/partner")
    protected List<PartnerSearchExportView> partners;
    @XmlElementWrapper(name = "speakers", required = true, namespace = "http://optimus.com/view/speaker")
    @XmlElement(name = "export", namespace = "http://company.com/view/speaker")
    protected List<SpeakerSearchExportView> speakers;

Method in question

private static PropertyName findJaxbPropertyName(Annotated ae, Class<?> aeType, String defaultName)
    {
        XmlAttribute attribute = ae.getAnnotation(XmlAttribute.class);
        if (attribute != null) {
            return _combineNames(attribute.name(), attribute.namespace(), defaultName);
        }
        XmlElement element = ae.getAnnotation(XmlElement.class);
        if (element != null) {
            return _combineNames(element.name(), element.namespace(), defaultName);
        }
        /* 11-Sep-2012, tatu: Wrappers should not be automatically used for renaming.
         *   At least not here (databinding core can do it if feature enabled).
         */
        /*
        XmlElementWrapper elementWrapper = ae.getAnnotation(XmlElementWrapper.class);
        if (elementWrapper != null) {
            return _combineNames(elementWrapper.name(), elementWrapper.namespace(), defaultName);
        }
        */
        XmlElementRef elementRef = ae.getAnnotation(XmlElementRef.class);
        if (elementRef != null) {
            if (!MARKER_FOR_DEFAULT.equals(elementRef.name())) {
                return _combineNames(elementRef.name(), elementRef.namespace(), defaultName);
            }
            if (aeType != null) {
                XmlRootElement rootElement = (XmlRootElement) aeType.getAnnotation(XmlRootElement.class);
                if (rootElement != null) {
                    String name = rootElement.name();
                    if (!MARKER_FOR_DEFAULT.equals(name)) {
                        return _combineNames(name, rootElement.namespace(), defaultName);
                    }
                    // Is there a namespace there to use? Probably not?
                    return new PropertyName(Introspector.decapitalize(aeType.getSimpleName()));
                }
            }
        }
        XmlValue valueInfo = ae.getAnnotation(XmlValue.class);
        if (valueInfo != null) {
            return new PropertyName("value");
        }

        return null;
    }

setNameUsedForXmlValue causes values to show up twice in the JSON output.

Example here I called setNameUsedForXmlValue("$") :

{
  "RequestId" : {
    "IdentificationID" : {
      "value" : "1d9d7f5d-9ec3-4143-b2b3-ec6bea4ea9d7",
      "$" : "1d9d7f5d-9ec3-4143-b2b3-ec6bea4ea9d7"
    }
  },
  "CommandType" : "GET_PERSON",
  "Person" : {
    "PersonId" : {
      "IdentificationID" : {
        "value" : "473efc90-f254-453c-94ab-592aac3d363b",
        "$" : "473efc90-f254-453c-94ab-592aac3d363b"
      }
    },
    "PersonSSNIdentification" : null
  }
}

Support namespace-defaulting part of `@XmlSchema` annotation

Looks like @XmlSchema may actually have some use for us, for namespace defaulting.
Although I am not a big fan of using package-annotations for anything, in this case it should be easy enough to add such support, for specific use case; and while there is potential for some conflicts (package vs super-classes), I suspect it's mostly a non-problem, and that @XmlSchema usage is from legacy- or JAXB-only use cases.

Using @XmlEnum to generate an enum of type Integer in json schema

I just posted an issue in jackson-module-jsonSchema.

FasterXML/jackson-module-jsonSchema#49

For completeness:

I have the following Java enum:

@XmlType
@XmlEnum(Integer.class)
public enum DefaultMessageVersion
{
    @XmlEnumValue("1") ONE;
}

That is producing the following json schema snippet:

"defaultMessageVersion" : {
"type" : "string",
"enum" : [ "1" ]
}

This is because (I believe) Jackson ignores the @XmlEnum annotation. I would like the outputted snippet to have "type" : "integer" (or int? I'm not 100% sure which it is).

I have posted the issue here at the request of cowtowncoder, and am hoping that I can submit a fix myself (with pointers on where to look), or that someone else with more knowledge and expertise can take care of it without too much trouble.

Add support for @XmlSeeAlso

If possible, it'd be great to make some use of @XmlSeeAlso; basically it could be used as sort of alias for @JsonSubTypes, to find concrete subtypes of abstract/base types

Version 2.7.2 give a java.lang.NoSuchMethodError on AnnotatedMember.getType

Using the following dependencies with a customer object mapper using jersey / wildfly 10
[email protected]
[email protected]
[email protected]
[email protected]

I have the following error when posting/putting on my web service when using a custom object mapper (see below)

java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.introspect.AnnotatedMember.getType()Lcom/fasterxml/jackson/databind/JavaType;

The same case works fine using the version 2.6 of all the above dependencies

@Provider
public class JacksonConfiguration implements ContextResolver<ObjectMapper>
{
    private final ObjectMapper objectMapper;

    public JacksonConfiguration() throws Exception
    {
        objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setTimeZone(TimeZone.getDefault());

        AnnotationIntrospector aiJaxb = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
        AnnotationIntrospector aiJackson = new JacksonAnnotationIntrospector();

        // first Jaxb, second Jackson annotations
        objectMapper.setAnnotationIntrospector(AnnotationIntrospector.pair(aiJaxb, aiJackson));
    }

    @Override
    public ObjectMapper getContext(Class<?> arg0)
    {
        return objectMapper;
    }
}

@JsonRootName Not Working for Collection Type

The following is the test case.


POJO Classs

@JsonRootName("Employee")
public class Employee implements Serializable {

     private String empName;
     private String empNo;
     public String getEmpName() {
          return empName;
     }
  public void setEmpName(String empName) {
          this.empName = empName;
     }
     public String getEmpNo() {
          return empNo;
     }
     public void setEmpNo(String empNo) {
          this.empNo = empNo;
     }
}

TEST CASE

public class EmployeeTest {
    @Test
    public void testEMPJsonData() throws  Exception{

        Employee emp =new Employee();
        emp.setEmpName("john");
        emp.setEmpNo("1234");
        emp.setPermentAddress("india");
        List<Employee> empList = new ArrayList<Employee>();
        empList.add(emp);
        /********** - JACKSON *******************/
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME, true);
        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
        mapper.setAnnotationIntrospector(new AnnotationIntrospectorPair(new  JacksonAnnotationIntrospector(), new JaxbAnnotationIntrospector(TypeFactory.defaultInstance())));
        mapper.writeValue(System.out, empList); 

    }
}

ouput

{"ArrayList":[{"empName":"john","empNo":"1234"}]}

Expected :

{"Employee":[{"empName":"john","empNo":"1234"}]}

why it behaving like this for collections?

The "Annotated" class in the dependency "jackson-databind" package has deprecated the getType() method Since 2.7

from the source code, jackson-module-jaxb-annotations depends on the "jackson-databind" package; for example, in https://github.com/FasterXML/jackson-module-jaxb-annotations/blob/master/src/main/java/com/fasterxml/jackson/module/jaxb/JaxbAnnotationIntrospector.java line 1606:

    protected JavaType _fullSerializationType(AnnotatedMember am) {
        return am.getType();
    }

However, when I cross-reference the AnnotatedMember class and its super class Annotated, from the code file: https://github.com/FasterXML/jackson-databind/blob/2.7/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java

Since line 65, the comment says:

    /**
     * @deprecated Since 2.7 Use {@link #getType()} instead. To be removed from 2.8.
     */
    @Deprecated
    public final JavaType getType(TypeBindings bogus) {
        return getType();
    }

Actually, I have met the relevant error when I am using the spring-boot (1.3.2.RELEASE) framework, which makes me a little freak out, and the error message looks like:

2016-02-25 08:28:04.093 ERROR 21983 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[servletContainer] : Servlet.service() for servlet [servletContainer] in context with path [] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.introspect.AnnotatedMember.getType()Lcom/fasterxml/jackson/databind/JavaType;] with root cause

java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.introspect.AnnotatedMember.getType()Lcom/fasterxml/jackson/databind/JavaType;
at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector._fullSerializationType(JaxbAnnotationIntrospector.java:1606) ~[jackson-module-jaxb-annotations-2.7.1.jar!/:2.7.1]
at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector._findContentAdapter(JaxbAnnotationIntrospector.java:1557) ~[jackson-module-jaxb-annotations-2.7.1.jar!/:2.7.1]

Can you evaluate if it is one bug, and could you provide one possible solution?

Thank you very much!

Jackson interpretation of XMLElements not compatible with JAXB

I have a very simple example of a class with a List member that uses polymorphism. The Java code looks like this:

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

  @XmlElements({
      @XmlElement(type = DesktopComputer.class, name = "desktop"),
      @XmlElement(type = LaptopComputer.class, name = "laptop")
  })
  private List computers;
  ...
}

JAXB serializes the output correctly (or at least in the way that I want it) like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
    <computers>
        <desktop id="computer-1">
            <location>Bangkok</location>
        </desktop>
        <desktop id="computer-2">
            <location>Pattaya</location>
        </desktop>
        <laptop id="computer-3">
            <vendor>Apple</vendor>
        </laptop>
    </computers>
</company>

Jackson JAXB annotations wraps each resulting element in the list in a redundant <computers> tag like this:

<?xml version='1.1' encoding='UTF-8'?><company><computers>
    <computers>
      <desktop id="computer-1">
        <location>Bangkok</location>
      </desktop>
    </computers>
    <computers>
      <desktop id="computer-2">
        <location>Pattaya</location>
      </desktop>
    </computers>
    <computers>
      <laptop id="computer-3">
        <vendor>Apple</vendor>
      </laptop>
    </computers>
  </computers>
</company>

How can I eliminate the extra wrapping Jackson is doing to generate the same shape as JAXB?

Incorrect JSON format with Jackson and JaxB Annotation Module

Very weird problem with Jackson and JaxB Annotation. See the issue with incorrect JSON format.

public interface All {
}
@XmlJavaTypeAdapter(value=AnyAdapter.class, type=Any.class)
public interface Any {
    public boolean isKnown();
}
@XmlJavaTypeAdapter(value=AnyAdapter.class, type=Any.class)
public abstract class Known implements Any {
    @Override
    @XmlTransient
    public boolean isKnown() {
        return false;
    }
    public Known(String type) {
        super();
        this.type = type;
        }
        String type;

        public String getType() {
            return type;
        }

    public void setType(String type) {
        this.type = type;
    }
    public abstract void validate();
}
@XmlRootElement
public class Resource extends Known implements All{
    public Resource(Integer resourceId, String resourceName) {
        super("Resource");
        this.resourceId = resourceId;
        this.resourceName = resourceName;
    }
    public Integer getResourceId() {
        return resourceId;
    }
    public void setResourceId(Integer resourceId) {
        this.resourceId = resourceId;
    }
    public String getResourceName() {
        return resourceName;
    }
    public void setResourceName(String resourceName) {
        this.resourceName = resourceName;
    }
    Integer resourceId;
    String  resourceName;
    @Override
    public void validate() {
    }
}
@XmlRootElement
public class ResourceList extends Known{
    public ResourceList(String name) {
        super("ResourceList");
        this.name = name;
    }
    List<All> resourceList;
    String name;
        @XmlElements({ 
            @XmlElement(name="resource", type=Resource.class), 
        })
    public List<All> getResourceList() {
        return resourceList;
    }
    public void setResourceList(List<All> resourceList) {
        this.resourceList = resourceList;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void validate() {
    }
}
public class JacksonJaxbAdapterImpl {

    public ObjectMapper initializeJackson() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.setSerializationInclusion(Include.NON_NULL);

        // Need to use JaxB Annotation.
        JaxbAnnotationModule module = new JaxbAnnotationModule();
        mapper.registerModule(module);

        SimpleModule mySimpleModule = new SimpleModule();
        mySimpleModule.addSerializer(Long.class, new ToStringSerializer());
        mySimpleModule.addSerializer(Double.class, new ToStringSerializer());
        mapper.registerModule(mySimpleModule);

        return mapper;
    }

    public void writeObject(Object target, Writer writer, boolean isPretty) throws Exception {
        ObjectMapper mapper = initializeJackson();
        try {
            if(isPretty) {
                mapper.writerWithDefaultPrettyPrinter().writeValue(writer,target);
            } else {
                mapper.writeValue(writer,target);
            }
        } catch (JsonGenerationException e) {
            throw new RuntimeException(e);
         } catch (JsonMappingException e) {
            throw new RuntimeException(e);
         } catch (IOException e) {
            throw new RuntimeException(e);
         }
    }

}

public class JacksonJaxbAdapterTest {
    public static void main(String[] args) throws Exception {
        JacksonJaxbAdapterTest test = new JacksonJaxbAdapterTest();

        ResourceList report = test.makeObject();
        StringWriter jacksonWriter = new StringWriter();

        JacksonJaxbAdapterImpl jacksonMainClass = new JacksonJaxbAdapterImpl();
        jacksonMainClass.initializeJackson();
        jacksonMainClass.writeObject(report, jacksonWriter, true);

        System.out.println(jacksonWriter);
    }

    ResourceList makeObject() {
        Resource primary = new Resource(12,"primary");
        Resource secondary = new Resource(13, "secondary");

        List<All> listofRes = new ArrayList<All>();
        listofRes.add(primary);
        listofRes.add(secondary);

        ResourceList report = new ResourceList("Daily");
        report.setResourceList(listofRes);

        return report;
    }
}

The JSON Output is with an extra unwanted "resource" tag.

{
  "name" : "Daily",
  "resourceList" : [ {
    "resource" : {
      "resourceId" : 12,
      "resourceName" : "primary",
      "type" : "Resource"
    }
  }, {
    "resource" : {
      "resourceId" : 13,
      "resourceName" : "secondary",
      "type" : "Resource"
    }
  } ],
  "type" : "ResourceList"
}

Remove the @XmlElements Tag (in ResourceList.java) and leave it with @xmlelement tag only- the JSON becomes perfect without the "resource" tag. @xmlelement(name="resource", type=Resource.class)

{
  "name" : "Daily",
  "resource" : [ {
    "resourceId" : 12,
    "resourceName" : "primary",
    "type" : "Resource"
  }, {
    "resourceId" : 13,
    "resourceName" : "secondary",
    "type" : "Resource"
  } ],
  "type" : "ResourceList"
}

Any Suggestion?

Support @XmlID, @XmlIDREF

(note: re-creation of [https://jira.codehaus.org/browse/JACKSON-442])


Now that Jackson 2.0 supports Object Identity, with the new '@XmlIdentityInfo' annotation (and all the machinery needed to support it), we could finally tackle these 2 tricky JAXB annotations.

XmlAnyElement with XmlElementRefs on non-collection property

I've a JAXB annotated class as below -

class Bean<R> {
    @XmlAnyElement(lax=true)
    @XmlElementRefs({
        @XmlElementRef(name="a", type=A.class),
        @XmlElementRef(name="b", type=B.class)
    })
    public R r;

    public String name;

    public Bean() { }

    public String getName() {return this.name;}

    public void setName(String name) {
            this.name = name;
    }
    public R getR() {
    return r;
}

public void setR(R r) {
    this.r = r;
}
}

A and B are annotated with XmlRootElement

Using the ObjectMapper with JacksonAnnotationIntrospector produces this JSON -

{"r":{"a":{"count":12}},"name":"test"} //Assuming A has a single int property 'count'.

I was wondering if it would be possible to remove 'r' and have this json?

{"a":{"count":12}},"name":"test"}

@XmlValue and @XmlAttribute break existing clients

Hello :)
I've upgraded from 1.9 to 2.0.5 and the property names of the marshalled objects changed.
For @XmlAttribute the JSON property name is prefixed with a "@" now and for @XmlValue the property is named "$" instead of "value".
Unfortunately this breaks existing clients and I can't find an existing configuration option to revert to the previous behaviour.

Is there a possible Workaround?
I've tried to change the AnnotationIntrospector order to have the JacksonAnnotationIntrospector as first and annotated the getter with @JsonProperty("name") in adition to the JAXB annotation. Unfortunately that doesn't change the behaviour.

Regards,
Andreas

JaxbAnnotationIntrospector creates an element from @XmlAttribute instead of an attribute

Expected: <Individual identifier="1"><givenName>Jay</givenName><surName>Unit</surName></Individual>
Actual: <Individual xmlns=""><identifier>1</identifier><givenName>Jay</givenName><surName>Unit</surName></Individual>

package com.example.tests;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import org.junit.Test;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import static junit.framework.Assert.assertEquals;

public class PersonTest {

@XmlRootElement(name = "Individual")
class MyPerson {

    @XmlAttribute(name = "identifier")
    private Long id;

    @XmlElement(name = "givenName")
    private String firstName;

    @XmlElement(name = "surName")
    private String lastName;

    public Long getId() {
        return id;
    }
    public void setId(final Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }
    public void setLastName(final String lastName) {
        this.lastName = lastName;
    }
}


@Test
public void testPersonAsXml() throws Exception {
    MyPerson person = new MyPerson();
    person.setId(1L);
    person.setFirstName("Jay");
    person.setLastName("Unit");

    XmlMapper mapper = new XmlMapper();
    mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());
    String actual = mapper.writeValueAsString(person);
    System.out.println("Person: " + actual);

    String expected = "<Individual identifier=\"1\"><givenName>Jay</givenName><surName>Unit</surName></Individual>";
    assertEquals(expected, actual);
}

}

Add a feature in `JaxbAnnotationIntrospector` to define meaning of `nillable=false` as "JsonInclude.NON_EMPTY"

(see #48, #49 for background)

Meaning of nillable property of @XmlElement and @XmlElements comes from XML Schema, and defines whether it is legal/mandatory to suppress writing out of empty collections. For Jackson this can be achieved by returning different inclusion criteria for a property annotated with one of these annotations.

For a longer discussion, see the original change #39 (for Jackson 2.5.1), which covers many aspects of handling.

Since I am not convinced that the change suggested in #48 / #49 is safe (although perhaps returning Include.NON_NULL might be acceptable, more so than NON_EMPTY), I will add a setting with which one can specifically define which JsonInclude.Incude value to use for nillable = false; default being null (meaning none). To do what #48 asks, Include.NON_EMPTY should be returned, or possibly Include.NON_NULL.

XMLElement required value

JaxB @ 2.25
JaxB-api @ 2.2
Jackson-core @ 2.3.2
Jackson-mapper @ 1.9
Jackson-databind @ 2.3.2
Jackson-xc @ 1.9

Trying to use JaxB to create my POJO classes which generate the @xmlelement annotations. Afterwards, I'm trying to get the JSON schema for the POJO class generated and the required field is not being brought over into the schema.

Example:

Generated POJO class:

// Some other stuff here.
@XMLRootElement(name = "Foo")
public class Foo {
    @XMLElement(name = "bar", required = true)
    @XMLJavaTypeAdapter(NormalizedStringAdapter.class)
    protected String bar;
    // Some other stuff here.
}

Caller class:

// Some other stuff here.
private ObjectMapper mapper;
public class Baz {
    public Baz() {
        mapper = new ObjectMapper();
        AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
        AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
        AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primaryIntrospector, secondaryIntropsector);
        mapper.setAnnotationIntrospector(pair);
    }
    public void someMethod() {
        JsonSchema jsonSchema = mapper.generateJsonSchema(Foo.class);
        System.out.println(jsonSchema.toString());
    }
}
// Some other stuff here.

Resulting JSON is

{"properties": {"bar": {"type": "string"}}}

What I want is

{"properties": {"bar": {"type": "string", "required": true}}}

What can I do to produce this value within the JSON?

Translation of XmlJavaTypeAdapter doesn't work like expected

Hello,

I have recognized with webron together (swagger team) that there is an issue with the translation of XmlJavaTypeAdapter to json.

I had registered the needed module (JaxbAnnotationModule) and generated the json.

The swagger json looks like

    "PackageNumber" : {
      "type" : "object",
      "properties" : {
        "value" : {
          "type" : "string"
        }
      }
    },

The same in xsd looks like

<xs:element minOccurs="0" name="packageNumber" type="xs:string"/>

You can see the xsd element is correct generated.

Some parts from the relevant classes:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = "http://namespace/v1")
@AssociationOverride(...)
public class FloatPackage extends AbstractItem {

    @NotNull
    @AttributeOverride(name = "value", column = @Column(name = "FGP_NUMBER", unique = true))
    private PackageNumber packageNumber;

The class PackageNumber:

@Embeddable
@XmlJavaTypeAdapter(PackageNumber.Adapter.class)
public class PackageNumber extends AbstractValueObject<String> {

AbstractValueObject:

@MappedSuperclass
public abstract class AbstractValueObject<V> {
    @Expose
    private V value;

Whole discussion with webron:
swagger-api/swagger-core#960

Conflicts on namespace and property with a same name

Hi,

I have a conflict between 2 properties when I trying to readValue from an XML.

Actually, with a default Jersey Jaxb implementation provider, we use a package-info.java file with @XmlSchema annotation but apparently jackson does not support it.

My test:

public class XmlMapperTest {

  @Test
  public void testFailure() throws Exception {
    XmlMapper xmlMapper = new XmlMapper();
    xmlMapper.setAnnotationIntrospector(
        new JaxbAnnotationIntrospector(xmlMapper.getTypeFactory()));

    String xml = "<root xmlns:bar=\"http://schemas.foo.com/batch/ns\">"
                 + "  <foo>baz</foo>"
                 + "  <bar:foo>qux</bar:foo>"
                 + "</root>";

    Pojo pojo = xmlMapper.readValue(xml, Pojo.class);

    assertEquals("baz", pojo.getFoo());
    assertEquals("qux", pojo.getBarFoo());
  }
}

My Pojo:

@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.PROPERTY)
class Pojo {

  private String foo;
  private String barFoo;

  public String getFoo() {
    return foo;
  }

  public void setFoo(String foo) {
    this.foo = foo;
  }

  @XmlElement(namespace = "http://schemas.foo.com/batch/ns", name = "foo")
  public String getBarFoo() {
    return barFoo;
  }

  public void setBarFoo(String barFoo) {
    this.barFoo = barFoo;
  }
}

JsonMappingException:

com.fasterxml.jackson.databind.JsonMappingException: Conflicting setter definitions for property "foo": com.github.nresni.model.Pojo#setFoo(1 params) vs com.github.nresni.model.Pojo#setBarFoo(1 params)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:269)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:143)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:342)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:2895)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2789)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1943)

Regards,

Nicolas

Jaxb annotations not working when used in conjunction with Jackson annotations

Using the Jackson2 ObjectMapper in Spring 3.2 doesn't recognize Jaxb annotations, when configured with an AnnotationIntrospector.Pair with both a JacksonAnnotationIntrospector and JaxbAnnotationIntrospector.

Excerpt from the Spring configuration:

<bean id="jacksonObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
  <property name="annotationIntrospector" ref="pairedAnnotationIntrospector"
</bean>

<bean id="pairedAnnotationIntrospector" class="com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair">
  <constructor-arg ref="jaxbAnnotationIntrospector" />   
  <constructor-arg ref="jacksonAnnotationIntrospector" />                 
</bean>

<bean id="jaxbAnnotationIntrospector" class="com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector" />
<bean id="jacksonAnnotationIntrospector" class="com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector" />

Jackson annotations (e.g. @JsonProperty) do work, whereas Jaxb annotations don't, even if there is no single Jackson annotation in the bean class. Jaxb works, if configured as the only annotation introspector.

Any thoughts on this?
This person seemed to have the same issue one year ago:
http://forum.spring.io/forum/spring-projects/web/125318-jackson2-jaxb-annotationintrospector-not-working-as-expected

@XMLElement name attribute still being ignored

So I have been trying to use RestEasy from Jboss AS7 (on openshift) with Jackson,

problem is I am trying to override the JSON element names with XMLElement(name="blah") and it ignores it complete, no matter what I do.

this happens on AS7.1.1.Final as well

@XmlJavaTypeAdapter does not work for values of Collections

Hi :)
It seems @XmlJavaTypeAdapter does not work when unmarshalling collections.
Tried with 2.0.5 and 2.1
Test project is at: https://github.com/rcpoison/JacksonTest

Exception:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token
 at [Source: java.io.StringReader@3c966db5; line: 1, column: 2] (through reference chain: org.afraid.poison.jacksontest.TestEntity["uuids"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:599)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:41)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
    at com.fasterxml.jackson.module.jaxb.deser.XmlAdapterJsonDeserializer.deserialize(XmlAdapterJsonDeserializer.java:82)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942)
    at org.afraid.poison.jacksontest.App.main(App.java:29)

if nillable is not ture , should return NON_NULL

if nillable is not ture , should return NON_NULL, this issue infect wildfly10 that resteasy api default ignore null filed when marshall object to json, suggest change code back to -->
return w.nillable() ? JsonInclude.Include.ALWAYS : JsonInclude.Include.NON_NULL;

Custom jaxb serializer ignored if other container type matches since 2.7.0

(moved from https://github.com/FasterXML/jackson/issues/46)

I am using a bean with a custom jaxb adapter:

@XmlElementWrapper(name = "links")
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
@XmlElement(name = "link")
private final List<Link> links;
private final Map<String, String> map;

This worked fine up until the last 2.6.x release. In 2.7.0, however, it seems that the map somehow prevents the custom serialiser to be used (still works fine as long as the Map and the List don't occur in the same file). When deserialising it seems that the custom adapter is used and this does not work with the non-custom serialiser. The fact that this only works (or does not work) one direction makes it look a bit "unintended."

I created a self contained example that shows this behaviour:

https://github.com/neumayer/jackson-example

If the jackson version in the pom file is changed to 2.6.x the test passes.

Any pointers are appreciated. I can take a closer look but I need some pointers on where to look, the adapter resolution code is not something I'm particularly familiar with.

Serializations fails with JaxbAnnotation Introspector and intermediary objects

(moved from FasterXML/jackson-databind#1143)


I have some JAXB annotated classes that I'm using the JaxbAnnotationIntrospector with that generally speaking works well, but when I have an intermediary object being created that then has a simple string value I have issues converting to JSON. A sample snippet demonstrating the problem is shown below.

Should this work or am I misusing something?

The exception is

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.lang.String and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: jackson.DTO["map"]->jackson.Intermediary["entry"]->java.util.ArrayList[0]->jackson.IntermediaryEntry["value"])
public class TestClass {

    @Test
    public void test() throws Exception {
        DTO d = new DTO();
        d.map = new HashMap<>();
        d.map.put("key", "value");

        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory()));

        mapper.writeValueAsString(d);
    }



    static public class MapAdapter extends XmlAdapter<Intermediary, Map<String, Object>> {

        @Override
        public Intermediary marshal(Map<String, Object> v) throws Exception {
            if(v == null) {
                return null;
            }
            Intermediary i = new Intermediary();
            for(Entry<String, Object> entry : v.entrySet()) {
                i.add(new IntermediaryEntry(entry));
            }

            return i;
        }

        @Override
        public Map<String, Object> unmarshal(Intermediary v) throws Exception {
            Map<String, Object> obj = new HashMap<>();
            if(v != null &&  v.entries != null) {

                for (IntermediaryEntry e : v.entries) {
                    obj.put(e.key, e.value);
                }
            }
            return Collections.unmodifiableMap(obj);
        }

    }

    @XmlAccessorType(XmlAccessType.FIELD)
    static public class Intermediary {
        @XmlElement(name="entry")
        List<IntermediaryEntry> entries;
        public void add(IntermediaryEntry e) {
            if(entries == null) {
                entries = new ArrayList<>();
            }
            entries.add(e);
        }
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    static public class IntermediaryEntry {
        String key;
        @XmlJavaTypeAdapter(PrimitiveStringOrDateAdapter.class)
        Object value;
        public IntermediaryEntry() {

        }
        public IntermediaryEntry(Entry<String, Object> entry) {
            this.key = entry.getKey();
            this.value = entry.getValue();
        }
    }

    static public class PrimitiveStringOrDateAdapter extends XmlAdapter<Object, Object> {
        @Override
        public Object unmarshal(Object v) throws Exception {
            if(v instanceof XMLGregorianCalendar) {
                return  ((XMLGregorianCalendar)v).toGregorianCalendar().getTime();
            }
            return v;
        }

        @Override
        public Object marshal(Object v) throws Exception {
            return v;
        }
    }

    public class DTO {
        @XmlElement
        @XmlJavaTypeAdapter(MapAdapter.class)
        Map<String, Object> map;
    }
}

Unable to bind JAXB annotation with List<Object>

I created a JAXB testcase to map xml to POJO and it works, but when I try to use the same object in Jackson.. it failed.

Here the code for JAXB


package com.test.options;

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

public class TestJAXBImporter {
/**
 * @param args
 */
public static void main(String[] args) throws Exception {
    String filename = "resources/options.xml";

    JAXBContext jc = JAXBContext.newInstance(Main.class);

    File xml = new File(filename);
    Unmarshaller unmarshaller = jc.createUnmarshaller();
    Main main = (Main) unmarshaller.unmarshal(xml);

    System.out.println(main.options);

    for (Option opt : main.options.option) {
        System.out.println("name=" + opt.name + "  value=" + opt.value);
    }

}
}

the output :

com.test.options.Options@d80be3
name=icon1  value=/image1.png
name=icon2  value=/image2.png

NOW for Jackson

I created a junit to reproduce the problem.


package com.fasterxml.jackson.dataformat.xml;

import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlValue;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

public class TestListDeserialization2 extends XmlTestBase
{
  /*
  /**********************************************************
  /* Helper types
  /**********************************************************
   */
    @XmlRootElement(name="main")
public static class Main {

    @JsonProperty("com.test.options")
    @XmlElement(name="com.test.options")
    public Options options;
}

public static class Options {

    @JsonProperty("option")
    @XmlElement(name="option")
    public List<Option> option;

}

public static class Option {

    @XmlAttribute
    public String name;

    @XmlValue
    public String value;
}

public static String getXML(){

    StringBuilder sb = new StringBuilder();

    sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("\n");
    sb.append("<main>").append("\n");
    sb.append(" <com.test.options>").append("\n");
    sb.append("     <option name=\"icon1\">/image1.png</option>").append("\n");
    sb.append("     <option name=\"icon2\">/image2.png</option>").append("\n");
    sb.append(" </com.test.options>").append("\n");
    sb.append("</main>").append("\n");

    return sb.toString();

}

/*
/**********************************************************
/* Unit tests
/**********************************************************
 */

private final XmlMapper MAPPER = new XmlMapper();
{
    // easier for eye:
    MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
}

 /* Issue#17 [https://github.com/FasterXML/jackson-dataformat-xml/issues/17]
  * 
  * Problems deserializing otherwise properly wrapped lists
  */
public void testList() throws Exception
{
    String xml = getXML();


    AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
    // if ONLY using JAXB annotations:
    //xmlMapper.setAnnotationIntrospector(introspector);
    // if using BOTH JAXB annotations AND Jackson annotations:
    AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();

    MAPPER.setAnnotationIntrospector(new AnnotationIntrospector.Pair(introspector, secondary));
    //MAPPER.setAnnotationIntrospector(new AnnotationIntrospector.Pair(secondary, introspector));



    Main result = MAPPER.readValue(xml, Main.class);
    assertNotNull(result);
    assertNotNull(result.options);
    assertNotNull(result.options.option);

    assertTrue(result.options.option.size()==2);
    assertEquals("icon1", result.options.option.get(0).name);
    assertEquals("/image1.png", result.options.option.get(0).value);

    assertEquals("icon2", result.options.option.get(1).name);
    assertEquals("/image2.png", result.options.option.get(2).value);
  }
}

the stacktrace

com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.fasterxml.jackson.dataformat.xml.TestListDeserialization2$Option] from JSON String; no single-String constructor/factory method (through reference chain:
com.fasterxml.jackson.dataformat.xml.Main["com.test.options"]>com.fasterxml.jackson.dataformat.xml.Options["option"])
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator._createFromStringFallbacks(StdValueInstantiator.java:419)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:295)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromString(BeanDeserializer.java:405)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:117)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:226)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:203)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:338)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:87)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:338)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:87)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:290)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:112)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2563)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1805)
at com.fasterxml.jackson.dataformat.xml.TestListDeserialization2.testList(TestListDeserialization2.java:99)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:243)
at junit.framework.TestSuite.run(TestSuite.java:238)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Add a way to configure name to use for `@XmlValue` annotated properties

(note: followup for #30)

It should be possible to override default name used for @XmlValue annotated properties -- "value" -- to be something else, using a new method in JaxbAnnotationIntrospector. Further, by specifying null as name, no implicit name should be associated, in which case default property name introspection should yield default name.

Note that this affects usage of JAXB annotations with non-XML content; with XML, no property name is used as value is included as character data.

Conflicting getter definitions Exception

Hi
when two properties having Different @XmlElementWrapper names and same @xmlelement name then exception occurring.

Exception: Caused by: java.lang.IllegalArgumentException: Conflicting getter definitions for property "Address"
example:

List<Address>  officeAddresses;
List<Address>  residenceAddresses;

    @XmlElement(name="Address")
    public List<Address> getResidenceAddresses() {
        return residenceAddresses;
    }

    @XmlElementWrapper(name="ResidenceAddress")
    public void setResidenceAddress(List<Address> residenceAddress) {
    this.residenceAddress= residenceAddress;
    }

    @XmlElement(name="Address")
    public List<Address> getOfficeAddresses() {
        return officeAddresses;
    }

    @XmlElementWrapper(name="OfficeAddress")
    public void setOfficeAddress(List<Address> officeAddresses) {
        this.officeAddresses= officeAddresses;
    }

How to handle this kind of things ?

eg:

<officeAddress>
     <address>
</officeAddress>

<residenceAddress>
   <address>
</residenceAddress>

Not able create JSON Response for Above cases using Jackson.

Jettison working fine for above kind of issues.

Default name ignored on @XmlElementWrapper

@XmlElementWrapper (with USE_WRAPPER_NAME_AS_PROPERTY_NAME enabled) only works if the name parameter is given an explicit value. By default it should use the property name, but Jackson uses the name given in the @xmlelement attribute instead. e.g.,

@xmlelement( name = "a" )
@XmlElementWrapper
public List< String > b;

Should result in:
"b": [ ]
but is instead:
"a": [ ]

I believe this is because JaxbAnnotationIntrospector.findWrapperName simply passes "" as the default name to _combineNames, which then fails the !wrapperName.hasSimpleName( ) test in POJOPropertiesCollector._renameWithWrappers( ).

XmlElementRef ignored if inside XmlElementWrapper

It seams that JaxbAnnotationIntrospector is ignoring @XmlElementRef annotations if they are coupled with @XmlElementWrapper. For example, take this two simple beans:

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Foo {

    @XmlElement
    public int getCount() { return count; }
    public void setCount(int count) { this.count = count; }

    @XmlElementWrapper(name="data")
    @XmlElementRef
    public List<Bar> getBars() { return bars; }
    public void setBars(List<Bar> bars) { this.bars = bars; }

    private int count;
    private List<Bar> bars;

}

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Bar {

    @XmlElement
    public String getFirst() { return first; }
    public void setFirst(String first) { this.first = first; }

    @XmlElement
    public String getSecond() { return second; }
    public void setSecond(String second) { this.second = second; }

    private String first;
    private String second;

}

and this data:

Foo foo = new Foo();
foo.setCount(7);
    List<Bar> bars = new ArrayList<Bar>();
        Bar one = new Bar();
        one.setFirst("One");
        one.setSecond("Only");
    bars.add(one);
        Bar two = new Bar();
        two.setFirst("Two");
        two.setSecond("Trees");
    bars.add(two);
foo.setBars(bars);

When serialized to XML (using JAXB.marshal(...)) output is fine:

<foo>
    <data>
        <bar>
            <first>One</first>
            <second>Only</second>
        </bar>
        <bar>
            <first>Two</first>
            <second>Trees</second>
        </bar>
    </data>
    <count>7</count>
</foo>

However, when serializing to JSON (using ObjectMapper.writeValue(...)) whole
"data" part is missing and output is just:

{
  "count" : 7
}

I've found out, that I can get correct JSON output if I use @XmlElement
instead of @XmlElementRef on Foo.bars:

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Foo {

    @XmlElement
    public int getCount() { return count; }
    public void setCount(int count) { this.count = count; }

    @XmlElementWrapper(name="data")
    @XmlElement
    public List<Bar> getBars() { return bars; }
    public void setBars(List<Bar> bars) { this.bars = bars; }

    private int count;
    private List<Bar> bars;

}

Output:

{
  "count" : 7,
  "data" : [ {
    "first" : "One",
    "second" : "Only"
  }, {
    "first" : "Two",
    "second" : "Trees"
  } ]
}

Unfortunatelly, changing @XmlElementRef to @XmlElement is not viable
solution in complex project.

Note: in all above examples MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME
was set to true.

JaxbAnnotationIntrospector creates wrong element names for @XmlElementWrapper and @XmlElement on a List property

Test Results:
Expected: <Individual><id>1</id><name>Jay Unit</name><offspring><kid><id>2</id><name>Junior Unit</name><offspring/></kid></offspring></Individual>
Actual: <Individual xmlns=""><id>1</id><name>Jay Unit</name><offspring><offspring><id>2</id><name>Junior Unit</name><offspring></offspring></offspring></offspring></Individual>

package com.example.tests;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import org.junit.Test;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

import static junit.framework.Assert.assertEquals;

public class PersonTest {

  @XmlRootElement(name = "Individual")
  class MyPerson {

    private Long id;

    private String name;

    @XmlElementWrapper(name = "offspring")
    @XmlElement(name = "kid")
    private List<MyPerson> children = new ArrayList<MyPerson>();


    public Long getId() {
        return id;
    }
    public void setId(final Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(final String name) {
        this.name = name;
    }

    public List<MyPerson> getChildren() {
        return children;
    }
    public void setChildren(final List<MyPerson> children) {
        this.children = children;
    }
}

@Test
public void testPersonAsXml() throws Exception {
    MyPerson child = new MyPerson();
    child.setId(2L);
    child.setName("Junior Unit");

    List<MyPerson> children = new ArrayList<MyPerson>();
    children.add(child);

    MyPerson person = new MyPerson();
    person.setId(1L);
    person.setName("Jay Unit");
    person.setChildren(children);

    XmlMapper mapper = new XmlMapper();
    mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector());

    String expected = "<Individual><id>1</id><name>Jay Unit</name>"
            + "<offspring><kid><id>2</id><name>Junior Unit</name><offspring/></kid></offspring></Individual>";
    String actual = mapper.writeValueAsString(person);
    assertEquals(expected, actual);
 }
}

JaxbAnnotationIntrospector creates an element from an attribute instead of an attribute

This is a duplicate of issue #6 for jackson version 2.3.2; the comments on that bug report ended with it not being reproducible.

Please see the junit test and classpath being used in my stackoverflow question: http://stackoverflow.com/questions/22433679/how-to-use-jacksons-jaxbannotationintrospector-correctly

Someone answered my stackoverflow question saying that com.fasterxml.jackson.dataformat.xml.jaxb.XmlJaxbAnnotationIntrospector seems to work fine. I confirmed that when I replace JaxbAnnotationIntrospector with XmlJaxbAnnotationIntrospector in my junit test I get the expected output.

Since there is a solution (use XmlJaxbAnnotationIntrospector) maybe the quickest fix is to update the documentation to suggest using XmlJaxbAnnotationIntrospector in cases where the XML output from JaxbAnnotationIntrospector is not in line with the JAXB annotations.

JaxbAnnotationIntrospector does not honor JsonInclude.Include.NON_EMPTY set on ObjectMapper

I am configuring a Jackson JSON provider with a JAX-RS application's getSingletons() method. Note setting of NON_EMPTY:

final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
final JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider(
    objectMapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);

Let's also define a piece of dummy data:

@XmlType
public class Data {
    private final List<Object> stuff = new java.util.ArrayList<Object>();

    @XmlElement
    public List<Object> getStuff() {
        return this.stuff;
    }
}

Given that stuff is empty, I would expect this to output an empty object. However, it instead outputs:

{
    "stuff": [ ]
}

I have isolated this behavior to JaxbAnnotationIntrospector.findSerializationInclusion. (This is the 2.4.4 version, but this function is currently unmodified on master.) This function receives the default value of NON_EMPTY, but will return NON_NULL as long as the XmlElement annotation is present.

Given that this behavior is unexpected given the configuration, and also conflicts with the JAXB behavior of not serializing the list, I believe this function should return NON_EMPTY instead of NON_NULL if:

  • XmlElementWrapper is not present. (JAXB would output an empty element with the wrapper's name.)
  • XmlElement is present.
  • The given default is NON_EMPTY.

@XmlElement type and @XmlJavaTypeAdapter annotations

Hi.

On one of my classes I got this annotation for JAXB:

@XmlElement(required = true, type = String.class, nillable = true)
public CustomObject getName()

in the package-info.java I defined an adapter to convert string to CustomObject and CustomObject to string

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(type = CustomObject.class, value = CustomObjectAdapter.class) })

When i tried to get JSON string from object using:

objectMapper.writeValueAsString(myObject);

I get an exception:
Caused by: java.lang.IllegalArgumentException: Illegal concrete-type annotation for method 'getName': class java.lang.String not a super-type of (declared) class com.copmany.CustomObject

for more information:

FasterXML/jackson-jaxrs-providers#36

I found in method: public Class<?> findSerializationType(Annotated a)
In file: JaxbAnnotationInspector.java
that you dont look for @XmlJavaTypeAdapter at the package level at all...
but this isn't right....

the @XmlJavaTypeAdapter in the package level always need to be checked because it very important for custom objects that returns many times. ( like custom UUIDs class)

Full stack trace:

com.fasterxml.jackson.databind.JsonMappingException: Illegal concrete-type annotation for method 'getName': class java.lang.String not a super-type of (declared) class com.copmany.CustomObject
at com.fasterxml.jackson.databind.SerializerProvider.createAndCacheUntypedSerializer(SerializerProvider.java:1042)
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:445)
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:599)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:93)
at com.fasterxml.jackson.databind.ObjectMapper.configAndWriteValue(ObjectMapper.java:2811)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2268)
at com.copmany.EntryPoint.getString(EntryPoint.java:217)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:88)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:613)
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:194)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:102)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:94)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:223)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:203)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:137)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:158)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:243)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:163)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:219)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:681)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1452)
at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:248)
at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:211)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1423)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:450)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:138)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:540)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:213)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1083)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:379)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:175)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1017)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:258)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:317)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:445)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:260)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:225)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:596)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:527)
at java.lang.Thread.run(Thread.java:780)
Caused by: java.lang.IllegalArgumentException: Illegal concrete-type annotation for method 'getName': class java.lang.String not a super-type of (declared) class com.copmany.CustomObject
at com.fasterxml.jackson.databind.ser.PropertyBuilder.findSerializationType(PropertyBuilder.java:187)
at com.fasterxml.jackson.databind.ser.PropertyBuilder.buildWriter(PropertyBuilder.java:80)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructWriter(BeanSerializerFactory.java:758)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:590)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:377)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:272)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer2(BeanSerializerFactory.java:217)
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:152)
at com.fasterxml.jackson.databind.SerializerProvider.createUntypedSerializer(SerializerProvider.java:1077)
at com.fasterxml.jackson.databind.SerializerProvider.createAndCacheUntypedSerializer(SerializerProvider.java:1037)
... 53 more

ObejectMapper accepts other elements than specified in @XmlElements

Hi,

I am working on project where we are using JSON <-> XML conversion. We are using auto-generated JAXB beans (generated by MOXY).

I found some inconsistency with xsd:choice in xsd. In this case Moxy will generate mapping like this:

@XmlElements({ @XmlElement(name = "Type1", type = Type1.class),
            @XmlElement(name = "Type2", type = Type2.class) },
                        @XmlElement(name = "TypeN", type = TypeN.class) })
List<Objects> type1OrType2OrTypeN;

For such mapping Jackson is expecting _JSON _like this:

{
    "type1OrType2OrTypeN":  [       
             {   "Type1": {}      },
             {   "Type2": {}    }      
      ]
}

It expect array which contains wrapping objects, where each object has specified type in key.

My findings:

  1. Jackon will accept also different values (no expected form XSD perspecive), such us: string ["txt","txt1"] or Object [{"Object":{}}] -> could it be fixed and restricted to only specified by @XmlElement ones??
  1. When I provide different JsonObject than Object, Type1 or Type2, it fails with no meaningful error message: Could not resolve type id 'Type3' into a subtype of [simple type, class java.lang.Object]. -> Could it be changed to error which will say which types are allowed?
  2. JsonSchema for this elements brings no information about elements types, it only says that it is type of array. I do not have idea how valid JsonSchema should look like it this case. Maybe anyOf with allowed types?

I've checked how moxy handle this scenario, since it has also JSON <-> XML feature, and it that is handled differently. There is sample of valid Json:

{
    "Type1":   [ {},  {} ],
        "Type2" : []
}

Instead of having intermediate array element to store Type1 or Type2, it uses separate arrays for each type. It makes JsonSchema simpler because it simply say that object can contain array of Type1 or/and array of Type2. So maybe it is the way how this @XmlElements can be handled also in Jackson?

I've attaching simple testing classes

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

    @XmlElements({ @XmlElement(name = "Type1", type = Type1.class),
            @XmlElement(name = "Type2", type = Type2.class) })
    private List<Object> items;

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this,
                ToStringStyle.SHORT_PREFIX_STYLE);
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    public static class Type1 {

        @XmlAttribute(name = "Code", required = true)
        protected String code;

        public String getCode() {
            return code;
        }

        public void setCode(String value) {
            this.code = value;
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this,
                    ToStringStyle.SHORT_PREFIX_STYLE);
        }

    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    public static class Type2 {

        @XmlAttribute(name = "Code", required = true)
        protected String code;

        public String getCode() {
            return code;
        }

        public void setCode(String value) {
            this.code = value;
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this,
                    ToStringStyle.SHORT_PREFIX_STYLE);
        }

    }

}
public class XmlWithChoiceTest {

    ObjectMapper om;

    @Before
    public void setUp() {

        om = new ObjectMapper();

        AnnotationIntrospector jaxbAnnotationIntrospector = new JaxbAnnotationIntrospector(
                om.getTypeFactory());
        AnnotationIntrospector jacksonAnnotationIntrospector = new JacksonAnnotationIntrospector();

        AnnotationIntrospector pair = AnnotationIntrospector.pair(
                jaxbAnnotationIntrospector, jacksonAnnotationIntrospector);
        om.setAnnotationIntrospector(pair);

    }

    @Test(expected = JsonMappingException.class)
    public void shouldNotAcceptObjectAndString() throws Exception {
        String src = "{\"items\":[{\"Type1\":{\"Code\":\"ABC\"}},{\"Type2\":{\"Code\":\"GHI\"}},\"ABC\",{\"Object\":{\"Code\":\"GHI\"}}]}";
        XmlWithChoice readValue = om.readValue(src, XmlWithChoice.class);
    }

    @Test
    public void itShouldFailWithSomeMeaningfullError() throws Exception {
        String src = "{\"items\":[{\"Type3\":{\"Code\":\"ABC\"}},{\"Type2\":{\"Code\":\"GHI\"}},\"ABC\",{\"Object\":{\"Code\":\"GHI\"}}]}";
        om.readValue(src, XmlWithChoice.class);
    }

    @Test
    public void generateJsonSchema() throws Exception {

        SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
        om.acceptJsonFormatVisitor(om.constructType(XmlWithChoice.class),
                visitor);
        JsonSchema finalSchema = visitor.finalSchema();
        String schema = om.writeValueAsString(finalSchema);
        fail("It says only that items is type 'array' without any details");

    }
}

Thanks,
Pawel

Add 'setFirstAsId' on JaxbAnnotationModule, to force always-as-id

Now that Jackson core has a setting to indicate that all references to an Object be serialized using just the id (as opposed to serializing full POJO first time encountered, then id afterwards), we need to expose this for JAXB annotations as well.

The problem is that since JAXB has no equivalent setting, we need some way to indicate which method should be used when handling identity via using @XmlID.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.