Skip to content

Commit

Permalink
Fix for FasterXML#336, multiple class adapted for the feature.
Browse files Browse the repository at this point in the history
- Accepts new annotation for collection type.
  • Loading branch information
perilbrain committed Aug 17, 2019
1 parent 6bd59fb commit 7c0a367
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ target
*.iml
*.ipr
*.iws
.idea
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.fasterxml.jackson.dataformat.xml;

import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.annotation.*;

/**
Expand Down Expand Up @@ -80,6 +82,15 @@ public PropertyName findRootName(AnnotatedClass ac)
}
return super.findRootName(ac);
}

@Override
public String getWrapperForIndexedType(AnnotatedClass ac){
JacksonXmlRootElement root = ac.getAnnotation(JacksonXmlRootElement.class);
if (root != null) {
return root.wrapperForIndexedType();
}
return JacksonXmlRootElement.DEFAULT_WRAPPER_NAME;
}

/*
/**********************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

/**
Expand Down Expand Up @@ -43,6 +45,8 @@ public interface XmlAnnotationIntrospector
public Boolean isOutputAsCData(Annotated ann);

public void setDefaultUseWrapper(boolean b);

public String getWrapperForIndexedType(AnnotatedClass ac);

/*
/**********************************************************************
Expand Down Expand Up @@ -135,6 +139,15 @@ public void setDefaultUseWrapper(boolean b) {
_xmlSecondary.setDefaultUseWrapper(b);
}
}

@Override
public String getWrapperForIndexedType(AnnotatedClass ac) {
String value = (_xmlPrimary == null) ? null : _xmlPrimary.getWrapperForIndexedType(ac);
if ((value == null) && (_xmlSecondary != null)) {
value = _xmlSecondary.getWrapperForIndexedType(ac);
}
return value;
}
}

/*
Expand Down Expand Up @@ -183,5 +196,10 @@ public Boolean isOutputAsCData(Annotated ann) {
public void setDefaultUseWrapper(boolean b) {
// not used with JAXB
}

@Override
public String getWrapperForIndexedType(AnnotatedClass ac) {
return JacksonXmlRootElement.DEFAULT_WRAPPER_NAME;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonXmlRootElement
{
public static String DEFAULT_WRAPPER_NAME="item";

String namespace() default "";
String localName() default "";
String wrapperForIndexedType() default DEFAULT_WRAPPER_NAME;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

/**
Expand Down Expand Up @@ -57,4 +58,9 @@ public Boolean isOutputAsCData(Annotated ann) {
public void setDefaultUseWrapper(boolean b) {
// nothing to do with JAXB
}

@Override
public String getWrapperForIndexedType(AnnotatedClass ac) {
return JacksonXmlRootElement.DEFAULT_WRAPPER_NAME;
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package com.fasterxml.jackson.dataformat.xml.ser;

import java.io.IOException;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;

import com.fasterxml.jackson.core.*;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.TokenStreamFactory;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.GeneratorSettings;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.ser.SerializerCache;
import com.fasterxml.jackson.databind.ser.SerializerFactory;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.fasterxml.jackson.dataformat.xml.util.StaxUtil;
import com.fasterxml.jackson.dataformat.xml.util.TypeUtil;
import com.fasterxml.jackson.dataformat.xml.util.XmlRootNameLookup;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

/**
* We need to override some parts of
* {@link com.fasterxml.jackson.databind.SerializerProvider}
Expand Down Expand Up @@ -68,7 +69,8 @@ public void serializeValue(JsonGenerator gen, Object value) throws IOException
_initWithRootName(xgen, rootName);
asArray = TypeUtil.isIndexedType(cls);
if (asArray) {
_startRootArray(xgen, rootName);
String indexedRootName = _rootNameLookup.findWrapperForIndexedType(getTypeOfCollection(value), _config);
_startRootArray(xgen, indexedRootName);
}
}

Expand Down Expand Up @@ -107,7 +109,8 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType,
_initWithRootName(xgen, rootName);
asArray = TypeUtil.isIndexedType(rootType);
if (asArray) {
_startRootArray(xgen, rootName);
String indexedRootName = _rootNameLookup.findWrapperForIndexedType(getTypeOfCollection(rootType), _config);
_startRootArray(xgen, indexedRootName);
}
}
if (ser == null) {
Expand All @@ -125,6 +128,17 @@ public void serializeValue(JsonGenerator gen, Object value, JavaType rootType,
}
}

protected Class<?> getTypeOfCollection(Object value){
Class<?> eleClass = value.getClass();
if(Collection.class.isAssignableFrom(eleClass)) {
Collection<?> collection = (Collection<?>) value;
Iterator<?> iterator = collection.iterator();
if (iterator.hasNext())
eleClass = iterator.next().getClass();
}
return eleClass;
}

protected void _serializeXmlNull(JsonGenerator jgen) throws IOException
{
// 14-Nov-2016, tatu: As per [dataformat-xml#213], we may have explicitly
Expand All @@ -139,11 +153,11 @@ protected void _serializeXmlNull(JsonGenerator jgen) throws IOException
super.serializeValue(jgen, null);
}

protected void _startRootArray(ToXmlGenerator xgen, QName rootName) throws IOException
protected void _startRootArray(ToXmlGenerator xgen, String rootName) throws IOException
{
xgen.writeStartObject();
// Could repeat root name, but what's the point? How to customize?
xgen.writeFieldName("item");
xgen.writeFieldName(rootName);
}

protected void _initWithRootName(ToXmlGenerator xgen, QName rootName) throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.type.ClassKey;
import com.fasterxml.jackson.databind.util.SimpleLookupCache;
import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

/**
* Helper class used for efficiently finding root element name used with
Expand All @@ -26,6 +27,7 @@ public class XmlRootNameLookup
* state
*/
protected final transient SimpleLookupCache<ClassKey,QName> _rootNames = new SimpleLookupCache<>(40, 200);
protected final transient SimpleLookupCache<ClassKey,String> _indexedWrapperNames = new SimpleLookupCache<>(10, 200);

public XmlRootNameLookup() { }

Expand Down Expand Up @@ -57,6 +59,27 @@ public QName findRootName(Class<?> rootType, MapperConfig<?> config)
}
return name;
}

public String findWrapperForIndexedType(JavaType rootType, MapperConfig<?> config) {
return findWrapperForIndexedType(rootType.getRawClass(), config);
}

public String findWrapperForIndexedType(Class<?> rootType, MapperConfig<?> config)
{
ClassKey key = new ClassKey(rootType);
String wrapperName;
synchronized (_indexedWrapperNames) {
wrapperName = _indexedWrapperNames.get(key);
}
if (wrapperName != null) {
return wrapperName;
}
wrapperName = findWrapperName(rootType, config);
synchronized (_indexedWrapperNames) {
_indexedWrapperNames.put(key, wrapperName);
}
return wrapperName;
}

// NOTE: needed to be synchronized in 2.6.4, but 2.7.0 adds a proper fix
// for annotation introspection hence not needed any more
Expand Down Expand Up @@ -102,4 +125,20 @@ private String findNamespace(AnnotationIntrospector ai, AnnotatedClass ann)
}
return null;
}

private String findWrapperName(Class<?> rootType, MapperConfig<?> config)
{
BeanDescription beanDesc = config.introspectClassAnnotations(rootType);
AnnotationIntrospector annotationIntrospector = config.getAnnotationIntrospector();
AnnotatedClass annotatedClass = beanDesc.getClassInfo();
for (AnnotationIntrospector intr : annotationIntrospector.allIntrospectors()) {
if (intr instanceof XmlAnnotationIntrospector) {
String ns = ((XmlAnnotationIntrospector) intr).getWrapperForIndexedType(annotatedClass);
if (ns != null) {
return ns;
}
}
}
return JacksonXmlRootElement.DEFAULT_WRAPPER_NAME;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.fasterxml.jackson.dataformat.xml.ser;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class TestSerializationCollection extends XmlTestBase {


@JacksonXmlRootElement(localName="person", namespace="http://example.org/person", wrapperForIndexedType = "Person")
static class Person {

@JacksonXmlProperty(isAttribute = true)
public Integer id;

public String n;

public Person(Integer id, String name) {
this.id = id;
this.n = name;
}
}

@JacksonXmlRootElement(localName = "persons")
static class PersonList extends ArrayList<Person>{}

public void testList() throws Exception
{
List<String> personNames = Arrays.asList("A", "B", "C");
PersonList personList = IntStream.range(0, personNames.size())
.mapToObj(count -> new Person(count, personNames.get(count)))
.collect(Collectors.toCollection(PersonList::new));
XmlMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(personList);
assertEquals("<persons><Person id=\"0\"><n>A</n></Person><Person id=\"1\"><n>B</n></Person><Person id=\"2\"><n>C</n></Person></persons>",
xml);
}
}

0 comments on commit 7c0a367

Please sign in to comment.