HHH-16280 Fix JacksonXmlFormatMapper handling of array data types
This commit is contained in:
parent
e675962779
commit
c6fa754f06
|
@ -72,6 +72,8 @@ dependencies {
|
||||||
testRuntimeOnly testLibs.weld
|
testRuntimeOnly testLibs.weld
|
||||||
testRuntimeOnly testLibs.wildFlyTxnClient
|
testRuntimeOnly testLibs.wildFlyTxnClient
|
||||||
testRuntimeOnly libs.jackson
|
testRuntimeOnly libs.jackson
|
||||||
|
testRuntimeOnly libs.jacksonXml
|
||||||
|
testRuntimeOnly libs.jacksonJsr310
|
||||||
|
|
||||||
testAnnotationProcessor project( ':hibernate-jpamodelgen' )
|
testAnnotationProcessor project( ':hibernate-jpamodelgen' )
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,24 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.type.format.jackson;
|
package org.hibernate.type.format.jackson;
|
||||||
|
|
||||||
import org.hibernate.type.format.FormatMapper;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.hibernate.type.descriptor.WrapperOptions;
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
|
import org.hibernate.type.format.FormatMapper;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.JsonToken;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
|
@ -24,20 +35,37 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public JacksonXmlFormatMapper() {
|
public JacksonXmlFormatMapper() {
|
||||||
this(new XmlMapper());
|
this( createXmlMapper() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
|
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static XmlMapper createXmlMapper() {
|
||||||
|
final XmlMapper xmlMapper = new XmlMapper();
|
||||||
|
// needed to automatically find and register Jackson's jsr310 module for java.time support
|
||||||
|
xmlMapper.findAndRegisterModules();
|
||||||
|
xmlMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
|
||||||
|
xmlMapper.enable( ToXmlGenerator.Feature.WRITE_NULLS_AS_XSI_NIL );
|
||||||
|
// Workaround for null vs empty string handling inside arrays,
|
||||||
|
// see: https://github.com/FasterXML/jackson-dataformat-xml/issues/344
|
||||||
|
final SimpleModule module = new SimpleModule();
|
||||||
|
module.addDeserializer( String[].class, new StringArrayDeserializer() );
|
||||||
|
xmlMapper.registerModule( module );
|
||||||
|
return xmlMapper;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (T) charSequence.toString();
|
return (T) charSequence.toString();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( javaType.getJavaType() ) );
|
return objectMapper.readValue(
|
||||||
|
charSequence.toString(),
|
||||||
|
objectMapper.constructType( javaType.getJavaType() )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (JsonProcessingException e) {
|
catch (JsonProcessingException e) {
|
||||||
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
|
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
|
||||||
|
@ -49,12 +77,35 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
|
||||||
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
|
else if ( javaType.getJavaTypeClass().isArray() ) {
|
||||||
|
if ( javaType.getJavaTypeClass().getComponentType().isEnum() ) {
|
||||||
|
// for enum arrays we need to explicitly pass Byte[] as the writer type
|
||||||
|
return writeValueAsString( value, javaType, Byte[].class );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writeValueAsString( value, javaType, javaType.getJavaType() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> String writeValueAsString(Object value, JavaType<T> javaType, Type type) {
|
||||||
try {
|
try {
|
||||||
return objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
|
return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
|
||||||
.writeValueAsString( value );
|
|
||||||
}
|
}
|
||||||
catch (JsonProcessingException e) {
|
catch (JsonProcessingException e) {
|
||||||
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
|
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class StringArrayDeserializer extends JsonDeserializer<String[]> {
|
||||||
|
@Override
|
||||||
|
public String[] deserialize(JsonParser jp, DeserializationContext deserializationContext) throws IOException {
|
||||||
|
final ArrayList<String> result = new ArrayList<>();
|
||||||
|
JsonToken token;
|
||||||
|
while ( ( token = jp.nextValue() ) != JsonToken.END_OBJECT ) {
|
||||||
|
if ( token.isScalarValue() ) {
|
||||||
|
result.add( jp.getValueAsString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray( String[]::new );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ dependencyResolutionManagement {
|
||||||
|
|
||||||
alias( "jackson" ).to ( "com.fasterxml.jackson.core", "jackson-databind" ).version( "2.14.1" )
|
alias( "jackson" ).to ( "com.fasterxml.jackson.core", "jackson-databind" ).version( "2.14.1" )
|
||||||
alias( "jacksonXml" ).to ( "com.fasterxml.jackson.dataformat", "jackson-dataformat-xml" ).version( "2.14.1" )
|
alias( "jacksonXml" ).to ( "com.fasterxml.jackson.dataformat", "jackson-dataformat-xml" ).version( "2.14.1" )
|
||||||
|
alias( "jacksonJsr310" ).to( "com.fasterxml.jackson.datatype", "jackson-datatype-jsr310" ).version( "2.14.1" )
|
||||||
alias( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "8.0.0.Final" )
|
alias( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "8.0.0.Final" )
|
||||||
|
|
||||||
alias( "ant" ).to( "org.apache.ant", "ant" ).version( "1.8.2" )
|
alias( "ant" ).to( "org.apache.ant", "ant" ).version( "1.8.2" )
|
||||||
|
|
Loading…
Reference in New Issue