Skip to content

Commit 822b121

Browse files
committed
HHH-16280 Fix JacksonXmlFormatMapper handling of array data types
1 parent ff19a91 commit 822b121

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

hibernate-core/hibernate-core.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ dependencies {
6868
testRuntimeOnly testLibs.weld
6969
testRuntimeOnly testLibs.wildFlyTxnClient
7070
testRuntimeOnly libs.jackson
71+
testRuntimeOnly libs.jacksonXml
72+
testRuntimeOnly libs.jacksonJsr310
7173

7274
testAnnotationProcessor project( ':hibernate-jpamodelgen' )
7375

hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java

+64-5
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
*/
77
package org.hibernate.type.format.jackson;
88

9-
import org.hibernate.type.format.FormatMapper;
9+
import java.lang.reflect.Type;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
1013
import org.hibernate.type.descriptor.WrapperOptions;
1114
import org.hibernate.type.descriptor.java.JavaType;
15+
import org.hibernate.type.format.FormatMapper;
1216

17+
import com.fasterxml.jackson.annotation.JsonCreator;
18+
import com.fasterxml.jackson.annotation.JsonInclude;
19+
import com.fasterxml.jackson.annotation.JsonProperty;
1320
import com.fasterxml.jackson.core.JsonProcessingException;
1421
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import com.fasterxml.jackson.databind.SerializationFeature;
1523
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
1624

1725
/**
@@ -24,10 +32,13 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
2432
private final ObjectMapper objectMapper;
2533

2634
public JacksonXmlFormatMapper() {
27-
this(new XmlMapper());
35+
this( new XmlMapper() );
2836
}
2937

3038
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
39+
// needed to automatically find and register Jackson's jsr310 module for java.time support
40+
objectMapper.findAndRegisterModules();
41+
objectMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
3142
this.objectMapper = objectMapper;
3243
}
3344

@@ -36,8 +47,25 @@ public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, Wrapper
3647
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
3748
return (T) charSequence.toString();
3849
}
50+
if ( javaType.getJavaTypeClass().isArray() && javaType.getJavaTypeClass().getComponentType() == String.class ) {
51+
final StringWrapper[] array = (StringWrapper[]) readValueFromString(
52+
charSequence,
53+
javaType,
54+
StringWrapper[].class
55+
);
56+
final List<String> list = new ArrayList<>( array.length );
57+
for ( StringWrapper sw : array ) {
58+
list.add( sw.getValue() );
59+
}
60+
//noinspection unchecked
61+
return (T) list.toArray( String[]::new );
62+
}
63+
return readValueFromString( charSequence, javaType, javaType.getJavaType() );
64+
}
65+
66+
private <T> T readValueFromString(CharSequence charSequence, JavaType<T> javaType, Type type) {
3967
try {
40-
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( javaType.getJavaType() ) );
68+
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( type ) );
4169
}
4270
catch (JsonProcessingException e) {
4371
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
@@ -49,12 +77,43 @@ public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapper
4977
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
5078
return (String) value;
5179
}
80+
else if ( javaType.getJavaTypeClass().isArray() ) {
81+
if ( javaType.getJavaTypeClass().getComponentType() == String.class ) {
82+
final String[] array = (String[]) value;
83+
final List<StringWrapper> list = new ArrayList<>( array.length );
84+
for ( String s : array ) {
85+
list.add( new StringWrapper( s ) );
86+
}
87+
return writeValueAsString( list.toArray( StringWrapper[]::new ), javaType, StringWrapper[].class );
88+
}
89+
else if ( javaType.getJavaTypeClass().getComponentType().isEnum() ) {
90+
// for enum arrays we need to explicitly pass Byte[] as the writer type
91+
return writeValueAsString( value, javaType, Byte[].class );
92+
}
93+
}
94+
return writeValueAsString( value, javaType, javaType.getJavaType() );
95+
}
96+
97+
private <T> String writeValueAsString(Object value, JavaType<T> javaType, Type type) {
5298
try {
53-
return objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
54-
.writeValueAsString( value );
99+
return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
55100
}
56101
catch (JsonProcessingException e) {
57102
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
58103
}
59104
}
105+
106+
@JsonInclude( JsonInclude.Include.NON_NULL )
107+
private static class StringWrapper {
108+
private final String value;
109+
110+
@JsonCreator
111+
public StringWrapper(@JsonProperty( "value" ) String value) {
112+
this.value = value;
113+
}
114+
115+
public String getValue() {
116+
return value;
117+
}
118+
}
60119
}

settings.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ dependencyResolutionManagement {
8484

8585
alias( "jackson" ).to ( "com.fasterxml.jackson.core", "jackson-databind" ).version( "2.14.1" )
8686
alias( "jacksonXml" ).to ( "com.fasterxml.jackson.dataformat", "jackson-dataformat-xml" ).version( "2.14.1" )
87+
alias( "jacksonJsr310" ).to( "com.fasterxml.jackson.datatype", "jackson-datatype-jsr310" ).version( "2.14.1" )
8788
alias( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "7.0.4.Final" )
8889

8990
alias( "ant" ).to( "org.apache.ant", "ant" ).version( "1.8.2" )

0 commit comments

Comments
 (0)