dapr / dapr/java-sdk

[RECALLED] Wrong value in state store when use custom json serializer

Abierto
#503 7 comentarios 0 reacciones 1 asignado Reclamado por @abogdanov37 Ver en GitHub
area/client kind/bug P1 size/XS triaged/resolved
Lenguaje dominante
Java
Estrellas
300
Forks
230
Merge medio
5 d 1 h
PR fusionados (30 d)
5

Descripción

I use dapr v1.0.0. I wrote the actor use Java SDK. To use the right OffsetDateTime class serialization I wrote a custom Json serializer `class ISO8601DaprObjectSerializer extends ObjectSerializer implements DaprObjectSerializer`.
## Expected Behavior
When I use this serializer I want to see JSON in state store
## Actual Behavior
Now in state store I see base64 encoded string. In this case, I should use a special deserialized object constructor with string parameter.
## Steps to Reproduce the Problem
When register actor add custom serializer
```
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.dapr.client.ObjectSerializer;
import io.dapr.client.domain.CloudEvent;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.regex.Pattern;

public class ISO8601DaprObjectSerializer extends ObjectSerializer implements DaprObjectSerializer {

private static final Pattern PATTERN_ISO8601 = Pattern.compile("^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$");
private static final DateTimeFormatter ISO8601 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX").withZone(ZoneId.of("UTC"));

private static final JavaTimeModule TIME_MODULE = new JavaTimeModule();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.registerModule(TIME_MODULE);

static {
TIME_MODULE.addSerializer(OffsetDateTime.class, new JsonSerializer<>() {
@Override
public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(offsetDateTime.format(ISO8601));
}

});
}

@Override
public byte[] serialize(Object state) throws IOException {
if (state == null) {
return null;
}

if (state.getClass().equals(Void.class)) {
return null;
}

// Have this check here to be consistent with deserialization (see deserialize() method below).
if (state instanceof byte[]) {
return (byte[]) state;
}

// Not string, not primitive, so it is a complex type: we use JSON for that.
return OBJECT_MAPPER.writeValueAsBytes(state);
}

@Override
public T deserialize(byte[] content, TypeRef type) throws IOException {
var javaType = OBJECT_MAPPER.constructType(type.getType());

if ((javaType == null) || javaType.isTypeOrSubTypeOf(Void.class)) {
return null;
}

if (javaType.isPrimitive()) {
return deserializePrimitives(content, javaType);
}

if (content == null) {
return (T) null;
}

// Deserialization of GRPC response fails without this check since it does not come as base64 encoded byte[].
if (javaType.hasRawClass(byte[].class)) {
return (T) content;
}

if (content.length == 0) {
return (T) null;
}

if (javaType.hasRawClass(CloudEvent.class)) {
return (T) CloudEvent.deserialize(content);
}

return OBJECT_MAPPER.readValue(content, javaType);
}

@Override
public String getContentType() {
return "application/json";
}

/**
* Parses a given String to the corresponding object defined by class.
*
* @param content Value to be parsed.
* @param javaType Type of the expected result type.
* @param Result type.
* @return Result as corresponding type.
* @throws IOException if cannot deserialize primitive time.
*/
private static T deserializePrimitives(byte[] content, JavaType javaType) throws IOException {
if ((content == null) || (content.length == 0)) {
if (javaType.hasRawClass(boolean.class)) {
return (T) Boolean.FALSE;
}

if (javaType.hasRawClass(byte.class)) {
return (T) Byte.valueOf((byte) 0);
}

if (javaType.hasRawClass(short.class)) {
return (T) Short.valueOf((short) 0);
}

if (javaType.hasRawClass(int.class)) {
return (T) Integer.valueOf(0);
}

if (javaType.hasRawClass(long.class)) {
return (T) Long.valueOf(0L);
}

if (javaType.hasRawClass(float.class)) {
return (T) Float.valueOf(0);
}

if (javaType.hasRawClass(double.class)) {
return (T) Double.valueOf(0);
}

if (javaType.hasRawClass(char.class)) {
return (T) Character.valueOf(Character.MIN_VALUE);
}

return null;
}

return OBJECT_MAPPER.readValue(content, javaType);
}

public static ObjectMapper getMapper() {
return OBJECT_MAPPER;
}
}
```

## Release Note
RELEASE NOTE: **FIX** Serialization bug in SDK where custom JSON serializer is handled as byte[]

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.