Skip to content

Improve serialization and deserialization of RawData #586

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

Merged
merged 1 commit into from
Nov 15, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -6,18 +6,15 @@
import com.arangodb.entity.ReplicationFactor;
import com.arangodb.entity.arangosearch.CollectionLink;
import com.arangodb.entity.arangosearch.FieldLink;
import com.arangodb.util.RawBytes;
import com.arangodb.util.RawJson;
import com.arangodb.internal.InternalResponse;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -29,23 +26,10 @@ public final class InternalDeserializers {
static final JsonDeserializer<RawJson> RAW_JSON_DESERIALIZER = new JsonDeserializer<RawJson>() {
@Override
public RawJson deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// TODO: find a way to access raw bytes directly
return RawJson.of(SerdeUtils.INSTANCE.writeJson(p.readValueAsTree()));
}
};

static final JsonDeserializer<RawBytes> RAW_BYTES_DESERIALIZER = new JsonDeserializer<RawBytes>() {
@Override
public RawBytes deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// TODO: find a way to access raw bytes directly
ByteArrayOutputStream os = new ByteArrayOutputStream();
try (JsonGenerator g = p.getCodec().getFactory().createGenerator(os)) {
g.writeTree(p.readValueAsTree());
}
return RawBytes.of(os.toByteArray());
}
};

static final JsonDeserializer<CollectionStatus> COLLECTION_STATUS = new JsonDeserializer<CollectionStatus>() {
@Override
public CollectionStatus deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@
import com.arangodb.entity.CollectionType;
import com.arangodb.entity.InvertedIndexPrimarySort;
import com.arangodb.entity.ReplicationFactor;
import com.arangodb.util.RawBytes;
import com.arangodb.util.RawJson;
import com.arangodb.internal.InternalRequest;
import com.arangodb.internal.InternalResponse;
@@ -22,12 +21,10 @@ enum InternalModule implements Supplier<Module> {
module = new SimpleModule();

module.addSerializer(RawJson.class, InternalSerializers.RAW_JSON_SERIALIZER);
module.addSerializer(RawBytes.class, InternalSerializers.RAW_BYTES_SERIALIZER);
module.addSerializer(InternalRequest.class, InternalSerializers.REQUEST);
module.addSerializer(CollectionType.class, InternalSerializers.COLLECTION_TYPE);

module.addDeserializer(RawJson.class, InternalDeserializers.RAW_JSON_DESERIALIZER);
module.addDeserializer(RawBytes.class, InternalDeserializers.RAW_BYTES_DESERIALIZER);
module.addDeserializer(CollectionStatus.class, InternalDeserializers.COLLECTION_STATUS);
module.addDeserializer(CollectionType.class, InternalDeserializers.COLLECTION_TYPE);
module.addDeserializer(ReplicationFactor.class, InternalDeserializers.REPLICATION_FACTOR);
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import com.arangodb.util.RawBytes;
import com.arangodb.util.RawJson;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
@@ -16,6 +17,7 @@

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -104,7 +106,11 @@ public byte[] serializeUserData(Object value) {
return serialize(null);
}
Class<?> clazz = value.getClass();
if (isManagedClass(clazz)) {
if (RawBytes.class.equals(clazz)) {
return ((RawBytes) value).get();
} else if (RawJson.class.equals(clazz) && JsonFactory.FORMAT_NAME_JSON.equals(mapper.getFactory().getFormatName())) {
return ((RawJson) value).get().getBytes(StandardCharsets.UTF_8);
} else if (isManagedClass(clazz)) {
return serialize(value);
} else {
return userSerde.serialize(value);
@@ -121,8 +127,13 @@ public byte[] serializeCollectionUserData(Iterable<?> value) {
}

@Override
@SuppressWarnings("unchecked")
public <T> T deserializeUserData(byte[] content, Class<T> clazz) {
if (isManagedClass(clazz)) {
if (RawBytes.class.equals(clazz)) {
return (T) RawBytes.of(content);
} else if (RawJson.class.equals(clazz) && JsonFactory.FORMAT_NAME_JSON.equals(mapper.getFactory().getFormatName())) {
return (T) RawJson.of(new String(content, StandardCharsets.UTF_8));
} else if (isManagedClass(clazz)) {
return deserialize(content, clazz);
} else {
return userSerde.deserialize(content, clazz, RequestContextHolder.INSTANCE.getCtx());
Original file line number Diff line number Diff line change
@@ -4,11 +4,9 @@
import com.arangodb.entity.arangosearch.CollectionLink;
import com.arangodb.entity.arangosearch.FieldLink;
import com.arangodb.internal.ArangoRequestParam;
import com.arangodb.util.RawBytes;
import com.arangodb.util.RawJson;
import com.arangodb.internal.InternalRequest;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

@@ -26,16 +24,6 @@ public void serialize(RawJson value, JsonGenerator gen, SerializerProvider seria
gen.writeTree(SerdeUtils.INSTANCE.parseJson(value.get()));
}
};
static final JsonSerializer<RawBytes> RAW_BYTES_SERIALIZER = new JsonSerializer<RawBytes>() {
@Override
public void serialize(RawBytes value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// TODO: find a way to append raw bytes directly
// see https://github.com/FasterXML/jackson-core/issues/914
try (JsonParser parser = gen.getCodec().getFactory().createParser(value.get())) {
gen.writeTree(parser.readValueAsTree());
}
}
};
static final JsonSerializer<InternalRequest> REQUEST = new JsonSerializer<InternalRequest>() {
@Override
public void serialize(InternalRequest value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@
package com.arangodb;

import com.arangodb.entity.*;
import com.arangodb.entity.AqlExecutionExplainEntity.ExecutionPlan;
import com.arangodb.entity.QueryCachePropertiesEntity.CacheMode;
import com.arangodb.internal.serde.InternalSerde;
import com.arangodb.model.*;
import com.arangodb.util.MapBuilder;
import com.arangodb.util.RawBytes;
@@ -668,6 +668,19 @@ void queryWithTTL(ArangoDatabaseAsync db) throws InterruptedException, Execution
assertThat(ex.getMessage()).isEqualTo("Response: 404, Error: 1600 - cursor not found");
}

@ParameterizedTest
@MethodSource("asyncDbs")
void queryRawBytes(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException {
InternalSerde serde = db.getSerde();
RawBytes doc = RawBytes.of(serde.serialize(Collections.singletonMap("value", 1)));
RawBytes res = db.query("RETURN @doc", RawBytes.class, Collections.singletonMap("doc", doc)).get()
.getResult().get(0);
JsonNode data = serde.parse(res.get());
assertThat(data.isObject()).isTrue();
assertThat(data.get("value").isNumber()).isTrue();
assertThat(data.get("value").numberValue()).isEqualTo(1);
}

@ParameterizedTest
@MethodSource("asyncDbs")
void changeQueryCache(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException {
13 changes: 13 additions & 0 deletions test-functional/src/test/java/com/arangodb/ArangoDatabaseTest.java
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@

import com.arangodb.entity.*;
import com.arangodb.entity.QueryCachePropertiesEntity.CacheMode;
import com.arangodb.internal.serde.InternalSerde;
import com.arangodb.model.*;
import com.arangodb.util.*;
import com.fasterxml.jackson.databind.JsonNode;
@@ -736,6 +737,18 @@ void queryWithTTL(ArangoDatabase db) throws InterruptedException {
}
}

@ParameterizedTest
@MethodSource("dbs")
void queryRawBytes(ArangoDatabase db) {
InternalSerde serde = db.getSerde();
RawBytes doc = RawBytes.of(serde.serialize(Collections.singletonMap("value", 1)));
RawBytes res = db.query("RETURN @doc", RawBytes.class, Collections.singletonMap("doc", doc)).next();
JsonNode data = serde.parse(res.get());
assertThat(data.isObject()).isTrue();
assertThat(data.get("value").isNumber()).isTrue();
assertThat(data.get("value").numberValue()).isEqualTo(1);
}

@ParameterizedTest
@MethodSource("dbs")
void changeQueryCache(ArangoDatabase db) {
Original file line number Diff line number Diff line change
@@ -37,8 +37,8 @@ void rawBytesSerde(ContentType type) {
InternalSerde s = new InternalSerdeProvider(type).create();
ObjectNode node = JsonNodeFactory.instance.objectNode().put("foo", "bar");
RawBytes raw = RawBytes.of(s.serialize(node));
byte[] serialized = s.serialize(raw);
RawBytes deserialized = s.deserialize(serialized, RawBytes.class);
byte[] serialized = s.serializeUserData(raw);
RawBytes deserialized = s.deserializeUserData(serialized, RawBytes.class);
assertThat(deserialized).isEqualTo(raw);
}