Skip to content

Commit

Permalink
yoj-json-jackson-v2: Add JsonConverter implementation using Jackson
Browse files Browse the repository at this point in the history
  • Loading branch information
nvamelichev committed Jan 26, 2024
1 parent 954ed1a commit da2d640
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 116 deletions.
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
<artifactId>yoj-repository-ydb-v2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-json-jackson-v2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-util</artifactId>
Expand Down
49 changes: 49 additions & 0 deletions json-jackson-v2/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>yoj-json-jackson-v2</artifactId>
<packaging>jar</packaging>

<parent>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-parent</artifactId>
<version>1.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<name>YOJ - JSON with Jackson 2.x</name>
<description>
Adds JSON support to YOJ (JsonConverter implementation) using Jackson 2.x as the underlying JSON library.
</description>

<dependencies>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-repository</artifactId>
</dependency>

<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package tech.ydb.yoj.repository.db.json;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Suppliers;
import lombok.NonNull;
import lombok.SneakyThrows;
import tech.ydb.yoj.repository.db.common.JsonConverter;

import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Supplier;

/**
* {@link JsonConverter YOJ JSON Converter} implementation using Jackson as the underlying JSON library.
* Use it to support JSON-valued fields ({@link tech.ydb.yoj.databind.schema.Column @Column(flatten=false)} composite
* objects and dynamic fields with type of interface/abstract class, e.g. {@link java.util.List}):
* <blockquote>
* <pre>
* CommonConverters.defineJsonConverter(new JacksonJsonConverter());
* </pre>
* </blockquote>
* <p>
* To customize the {@link ObjectMapper} being created, create a subclass of {@code JacksonJsonConverter} and override
* its {@link #createObjectMapper()} method.
*/
public class JacksonJsonConverter implements JsonConverter {
private final Supplier<ObjectMapper> mapper = Suppliers.memoize(this::createObjectMapper);

@Override
@SneakyThrows
public final String toJson(@NonNull Type type, @Nullable Object o) {
return mapper().writerFor(mapper().getTypeFactory().constructType(type)).writeValueAsString(o);
}

@Override
@SneakyThrows
public final Object fromJson(@NonNull Type type, @NonNull String content) {
return mapper().readerFor(mapper().getTypeFactory().constructType(type)).readValue(content);
}

@Override
@SneakyThrows
public final Object fromObject(@NonNull Type type, @Nullable Object content) {
JavaType jacksonType = mapper().getTypeFactory().constructType(type);
return content != null
? mapper().convertValue(content, jacksonType)
: (jacksonType.isCollectionLikeType() ? List.of() : Map.of());
}

public String toString() {
return getClass().getSimpleName();
}

protected final ObjectMapper mapper() {
return mapper.get();
}

protected ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setTimeZone(TimeZone.getDefault());
mapper.registerModule(new Jdk8Module());
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new SimpleModule()
.addAbstractTypeMapping(Set.class, LinkedHashSet.class)
.addAbstractTypeMapping(Map.class, LinkedHashMap.class)
.addAbstractTypeMapping(List.class, ArrayList.class)
);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.INDENT_OUTPUT, false);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(Visibility.ANY)
.withGetterVisibility(Visibility.NONE)
.withIsGetterVisibility(Visibility.NONE)
.withSetterVisibility(Visibility.NONE)
);
return mapper;
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<modules>
<module>bom</module>
<module>databind</module>
<module>json-jackson-v2</module>
<module>repository</module>
<module>repository-test</module>
<module>repository-inmemory</module>
Expand Down
5 changes: 5 additions & 0 deletions repository-inmemory/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
<artifactId>yoj-repository-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-json-jackson-v2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import tech.ydb.yoj.repository.db.Table;
import tech.ydb.yoj.repository.db.TableQueryBuilder;
import tech.ydb.yoj.repository.db.TxOptions;
import tech.ydb.yoj.repository.db.common.CommonConverters;
import tech.ydb.yoj.repository.db.json.JacksonJsonConverter;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.BubbleTable;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.ComplexTable;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.IndexedTable;
import tech.ydb.yoj.repository.test.sample.TestJsonConverter;
import tech.ydb.yoj.repository.test.sample.model.Bubble;
import tech.ydb.yoj.repository.test.sample.model.Complex;
import tech.ydb.yoj.repository.test.sample.model.EntityWithValidation;
Expand All @@ -27,7 +28,7 @@

public class TestInMemoryRepository extends InMemoryRepository {
static {
TestJsonConverter.register();
CommonConverters.defineJsonConverter(new JacksonJsonConverter());
}

@Override
Expand Down

This file was deleted.

5 changes: 5 additions & 0 deletions repository-ydb-v1/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
<artifactId>yoj-repository-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-json-jackson-v2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import tech.ydb.yoj.repository.db.Table;
import tech.ydb.yoj.repository.db.TableQueryBuilder;
import tech.ydb.yoj.repository.db.TxOptions;
import tech.ydb.yoj.repository.db.common.CommonConverters;
import tech.ydb.yoj.repository.db.json.JacksonJsonConverter;
import tech.ydb.yoj.repository.db.statement.Changeset;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.BubbleTable;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.ComplexTable;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.IndexedTable;
import tech.ydb.yoj.repository.test.sample.TestEntityOperations.Supabubble2Table;
import tech.ydb.yoj.repository.test.sample.TestJsonConverter;
import tech.ydb.yoj.repository.test.sample.model.Bubble;
import tech.ydb.yoj.repository.test.sample.model.Complex;
import tech.ydb.yoj.repository.test.sample.model.EntityWithValidation;
Expand All @@ -34,7 +35,7 @@

public class TestYdbRepository extends YdbRepository {
static {
TestJsonConverter.register();
CommonConverters.defineJsonConverter(new JacksonJsonConverter());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
import tech.ydb.yoj.databind.schema.ObjectSchema;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.repository.db.Entity;
import tech.ydb.yoj.repository.db.common.CommonConverters;
import tech.ydb.yoj.repository.db.exception.ConversionException;
import tech.ydb.yoj.repository.test.sample.TestJsonConverter;
import tech.ydb.yoj.repository.db.json.JacksonJsonConverter;
import tech.ydb.yoj.repository.test.sample.model.NonDeserializableObject;
import tech.ydb.yoj.repository.ydb.yql.YqlPrimitiveType;
import tech.ydb.yoj.repository.ydb.yql.YqlType;
Expand All @@ -30,7 +31,7 @@
public class YqlTypeTest {
static {
FieldValueType.registerStringValueType(UUID.class);
TestJsonConverter.register();
CommonConverters.defineJsonConverter(new JacksonJsonConverter());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
import tech.ydb.yoj.databind.schema.Column;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.repository.DbTypeQualifier;
import tech.ydb.yoj.repository.test.sample.TestJsonConverter;
import tech.ydb.yoj.repository.db.common.CommonConverters;
import tech.ydb.yoj.repository.db.json.JacksonJsonConverter;
import tech.ydb.yoj.repository.ydb.DbType;

import java.lang.reflect.Type;
Expand All @@ -32,7 +33,7 @@
public class YqlTypeAllTypesTest {
static {
FieldValueType.registerStringValueType(UUID.class);
TestJsonConverter.register();
CommonConverters.defineJsonConverter(new JacksonJsonConverter());
}

private static final Map<String, Object> OBJECT_VALUE = Map.of("string", "Unnamed", "number", 11, "boolean", true);
Expand Down
5 changes: 5 additions & 0 deletions repository-ydb-v2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
<artifactId>yoj-repository-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>tech.ydb.yoj</groupId>
<artifactId>yoj-json-jackson-v2</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
Expand Down
Loading

0 comments on commit da2d640

Please sign in to comment.