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

HHH-16280 Fix Jackson XML mapper support for Oracle Array data types #6229

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions hibernate-core/hibernate-core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ dependencies {
testRuntimeOnly testLibs.weld
testRuntimeOnly testLibs.wildFlyTxnClient
testRuntimeOnly libs.jackson
testRuntimeOnly libs.jacksonXml
testRuntimeOnly libs.jacksonJsr310

testAnnotationProcessor project( ':hibernate-jpamodelgen' )

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@
*/
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.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.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
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.ser.ToXmlGenerator;

/**
* @author Christian Beikov
Expand All @@ -24,20 +35,37 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
private final ObjectMapper objectMapper;

public JacksonXmlFormatMapper() {
this(new XmlMapper());
this( createXmlMapper() );
}

public JacksonXmlFormatMapper(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
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
return (T) charSequence.toString();
}
try {
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( javaType.getJavaType() ) );
return objectMapper.readValue(
charSequence.toString(),
objectMapper.constructType( javaType.getJavaType() )
);
}
catch (JsonProcessingException e) {
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
Expand All @@ -49,12 +77,35 @@ public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapper
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
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 {
return objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
.writeValueAsString( value );
return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
}
catch (JsonProcessingException 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 );
}
}
}
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ dependencyResolutionManagement {

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( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "8.0.0.Final" )
alias( "jacksonJsr310" ).to( "com.fasterxml.jackson.datatype", "jackson-datatype-jsr310" ).version( "2.14.1" )
alias( "validator" ).to( "org.hibernate.validator", "hibernate-validator" ).version( "7.0.4.Final" )

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

Expand Down