Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for #356, multiple class adapted for the feature. #357

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
perilbrain marked this conversation as resolved.
Show resolved Hide resolved
_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);
}
}