Hi,

We are currently adapting a library based on XML messages to JSON. To ease 
ourselves in we are trying to piggy back off the XML annotations as much as 
possible and minise the number of mixins that need to be created.

We are running into a problem with collections, specificly those that have 
an attached xml adapter. The attached test demonstrates the problem. The 
class being serialized has four lists. The generic list is there as a base 
line, we haven't encountered it as a use case. We would expect the other 
three lists to produce the same output, but they don't. The adapted list, 
which is the main use case, gives the same output as the generic list. The 
other adapted list ends up lossing all the details from the contained 
elements. Only the list without an adpater outputs everything.

We are currently using version 2.8.6. 


   - Has this been fixed in a newer version?
   - If not, what are the technical limitations behind this issue?


Cheers
David

-- 
You received this message because you are subscribed to the Google Groups 
"jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.
package com.enactor.core.json;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
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 com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

import junit.framework.TestCase;

/**
 *
 * @author David Driver
 */
public class JsonCollectionTest
		extends TestCase {

	public void testSerialize() throws Exception {
		LibraryClass main = new LibraryClass();
		main.addChild(new LibraryBean("Child"));
		main.addChild(new LibraryCount(1234));
		System.out.println(createObjectMapper().writeValueAsString(main));
	}

	private ObjectMapper createObjectMapper() {
		ObjectMapper mapper = new ObjectMapper();
		JaxbAnnotationIntrospector primary = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
		primary.setNameUsedForXmlValue(null);
		mapper.setSerializationInclusion(Include.NON_EMPTY)
				.enable(SerializationFeature.INDENT_OUTPUT)
				.enable(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)
				.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
				// JAXB annotations will be inspected before jackson annotations
				.setAnnotationIntrospector(AnnotationIntrospectorPair.create(primary, new JacksonAnnotationIntrospector()))
				.addMixIn(TheMarkerInterface.class, CustomTypeIdMixin.class);
		return mapper;
	}

	@JsonPropertyOrder(value = { "@customType" }, alphabetic = true)
	@JsonTypeInfo(use = Id.CUSTOM, property = "@customType")
	@JsonTypeIdResolver(CustomTypeResolver.class)
	@JsonInclude(Include.NON_EMPTY)
	private interface CustomTypeIdMixin {

	}

	private static class CustomTypeResolver
			implements TypeIdResolver {

		private static final String RESOLVER_DESCRIPTION = "Example.CustomTypeResolver";

		private JavaType baseType;

		@Override
		public void init(JavaType baseType) {
			this.baseType = baseType;
			System.out.println(baseType);
		}

		@Override
		public String idFromBaseType() {
			return idFromValueAndType(null, baseType.getRawClass());
		}

		@Override
		public String idFromValueAndType(Object value, Class<?> suggestedType) {
			if (value == null) {
				return suggestedType.getSimpleName();
			}
			return idFromValue(value);
		}

		@Override
		public String idFromValue(Object value) {
			if (value instanceof TheMarkerInterface) {
				String id = createJAXBName(value.getClass());
				if (id != null) {
					return id;
				}
				return value.getClass().getSimpleName();
			}
			return null;
		}

		private String createJAXBName(Class<?> clazz) {
			XmlRootElement root = clazz.getAnnotation(XmlRootElement.class);
			if (root != null) {
				return "{" + root.namespace() + "}" + root.name();
			}

			XmlType type = clazz.getAnnotation(XmlType.class);
			if (type != null) {
				return "{" + type.namespace() + "}" + type.name();
			}
			return null;
		}

		@Override
		public JavaType typeFromId(DatabindContext context, String id) throws IOException {
			// Not being tested
			return null;
		}

		@Override
		public String getDescForKnownTypeIds() {
			return RESOLVER_DESCRIPTION;
		}

		@Override
		public Id getMechanism() {
			return Id.CUSTOM;
		}
	}

	private static interface TheMarkerInterface {
		// Interface that is part of the library
	}

	@XmlAccessorType(XmlAccessType.FIELD)
	@XmlType(name = "LibraryClass", namespace = "test")
	@XmlRootElement(name = "libraryClass", namespace = "test")
	private static class LibraryClass
			implements TheMarkerInterface {

		@XmlElement(name = "generic")
		private List<Object> genericList = new ArrayList<>();
		@XmlElement(name = "marked")
		private List<TheMarkerInterface> markedList = new ArrayList<>();
		@XmlElement(name = "adapted")
		@XmlJavaTypeAdapter(value = IdempotentAdapter.class)
		private List<TheMarkerInterface> adaptedList = new ArrayList<>();
		@XmlElement(name = "markedAdapted")
		@XmlJavaTypeAdapter(value = MarkedIdempotentAdapter.class)
		private List<TheMarkerInterface> markedAdaptedList = new ArrayList<>();

		public void addChild(TheMarkerInterface child) {
			genericList.add(child);
			markedList.add(child);
			adaptedList.add(child);
			markedAdaptedList.add(child);
		}

		public List<Object> getGenericList() {
			return genericList;
		}

		public List<TheMarkerInterface> getMarkedList() {
			return markedList;
		}

		public List<TheMarkerInterface> getAdaptedList() {
			return adaptedList;
		}

		public List<TheMarkerInterface> getMarkedAdaptedList() {
			return markedAdaptedList;
		}
	}

	@XmlAccessorType(XmlAccessType.FIELD)
	@XmlType(name = "LibraryBean", namespace = "test")
	@XmlRootElement(name = "libraryBean", namespace = "test")
	private static class LibraryBean
			implements TheMarkerInterface {

		private String value;

		public LibraryBean(String value) {
			this.value = value;
		}

		public String getValue() {
			return value;
		}

	}

	@XmlAccessorType(XmlAccessType.FIELD)
	@XmlType(name = "LibraryCount", namespace = "test")
	@XmlRootElement(name = "libraryCount", namespace = "test")
	private static class LibraryCount
			implements TheMarkerInterface {

		private long value;

		public LibraryCount(long value) {
			this.value = value;
		}

		public long getValue() {
			return value;
		}

	}

	private static class IdempotentAdapter<E>
			extends XmlAdapter<E, E> {

		@Override
		public E unmarshal(E v) throws Exception {
			return v;
		}

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

	}

	private static class MarkedIdempotentAdapter<E extends TheMarkerInterface>
			extends IdempotentAdapter<E> {

	}

}
[simple type, class com.enactor.core.json.JsonCollectionTest$TheMarkerInterface]
[simple type, class com.enactor.core.json.JsonCollectionTest$TheMarkerInterface]
[simple type, class com.enactor.core.json.JsonCollectionTest$TheMarkerInterface]
[simple type, class com.enactor.core.json.JsonCollectionTest$TheMarkerInterface]
[simple type, class com.enactor.core.json.JsonCollectionTest$LibraryClass]
{
  "@customType" : "{test}libraryClass",
  "adapted" : [ {
    "value" : "Child"
  }, {
    "value" : 1234
  } ],
  "generic" : [ {
    "value" : "Child"
  }, {
    "value" : 1234
  } ],
  "marked" : [ {
    "@customType" : "{test}libraryBean",
    "value" : "Child"
  }, {
    "@customType" : "{test}libraryCount",
    "value" : 1234
  } ],
  "markedAdapted" : [ {
    "@customType" : "{test}libraryBean"
  }, {
    "@customType" : "{test}libraryCount"
  } ]
}

Reply via email to