
io.fluxcapacitor.javaclient.common.serialization.jackson.JacksonSerializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client Show documentation
Show all versions of java-client Show documentation
Default Java client library for interfacing with Flux Capacitor.
/*
* Copyright (c) Flux Capacitor IP B.V. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.fluxcapacitor.javaclient.common.serialization.jackson;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.fluxcapacitor.common.api.Data;
import io.fluxcapacitor.common.api.SerializedObject;
import io.fluxcapacitor.common.api.search.SerializedDocument;
import io.fluxcapacitor.common.reflection.ReflectionUtils;
import io.fluxcapacitor.common.search.Inverter;
import io.fluxcapacitor.common.search.JacksonInverter;
import io.fluxcapacitor.common.serialization.JsonUtils;
import io.fluxcapacitor.javaclient.common.serialization.AbstractSerializer;
import io.fluxcapacitor.javaclient.common.serialization.ContentFilter;
import io.fluxcapacitor.javaclient.common.serialization.DeserializationException;
import io.fluxcapacitor.javaclient.common.serialization.DeserializingObject;
import io.fluxcapacitor.javaclient.common.serialization.casting.Caster;
import io.fluxcapacitor.javaclient.common.serialization.casting.CasterChain;
import io.fluxcapacitor.javaclient.common.serialization.casting.Converter;
import io.fluxcapacitor.javaclient.persisting.search.DocumentSerializer;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Type;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Stream;
import static io.fluxcapacitor.common.ObjectUtils.memoize;
import static java.lang.String.format;
@Slf4j
public class JacksonSerializer extends AbstractSerializer implements DocumentSerializer {
public static JsonMapper defaultObjectMapper = JsonUtils.writer;
@Getter
private final ObjectMapper objectMapper;
@Delegate
private final ContentFilter contentFilter;
private final Function typeCache = memoize(this::getJavaType);
private final Function typeStringCache = memoize(this::getCanonicalType);
private final Caster> jsonNodeUpcaster;
private final Inverter inverter;
public JacksonSerializer() {
this(Collections.emptyList());
}
public JacksonSerializer(Collection> casterCandidates) {
this(defaultObjectMapper, casterCandidates);
}
public JacksonSerializer(JsonMapper objectMapper) {
this(objectMapper, Collections.emptyList());
}
public JacksonSerializer(JsonMapper objectMapper, Collection> casterCandidates) {
this(objectMapper, casterCandidates, new ObjectNodeConverter(objectMapper));
}
public JacksonSerializer(JsonMapper objectMapper, Collection> casterCandidates, Converter converter) {
super(casterCandidates, converter, Data.JSON_FORMAT);
this.objectMapper = objectMapper;
this.contentFilter = new JacksonContentFilter(objectMapper.copy());
this.jsonNodeUpcaster = CasterChain.create(casterCandidates, JsonNode.class, false);
this.inverter = new JacksonInverter(objectMapper);
}
@Override
protected String asString(Type type) {
return typeStringCache.apply(type);
}
@Override
protected byte[] doSerialize(Object object) throws Exception {
return objectMapper.writeValueAsBytes(object);
}
@Override
protected Object doDeserialize(Data data, String type) throws Exception {
return objectMapper.readValue(data.getValue(), typeCache.apply(type));
}
@SneakyThrows
@Override
protected JsonNode asIntermediateValue(Object input) {
return input instanceof byte[]
? objectMapper.readTree((byte[]) input)
: objectMapper.convertValue(input, JsonNode.class);
}
@Override
protected boolean isKnownType(String type) {
try {
typeCache.apply(type);
return true;
} catch (Exception e) {
return false;
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
protected Stream> deserializeUnknownType(SerializedObject s) {
SerializedObject jsonNode =
s.withData(new Data<>(s.data().getValue(), JsonNode.class.getName(), 0, getFormat()));
return Stream.of(new DeserializingObject(jsonNode, (Function, Object>) type -> {
try {
return convert(objectMapper.readTree(jsonNode.data().getValue()), type);
} catch (Exception e) {
throw new DeserializationException(format("Could not deserialize a %s to a %s. Invalid json?",
type, s.data().getType()), e);
}
}));
}
protected JavaType getJavaType(String type) {
return objectMapper.getTypeFactory().constructFromCanonical(type);
}
protected String getCanonicalType(Type type) {
return objectMapper.constructType(type).toCanonical();
}
@Override
public SerializedDocument toDocument(Object value, String id, String collection, Instant timestamp, Instant end) {
return inverter.toDocument(value, getTypeString(value), getRevisionNumber(value), id, collection, timestamp,
end);
}
@Override
public T fromDocument(SerializedDocument document) {
return jsonNodeUpcaster.cast(Stream.of(inverter.fromDocument(document))).findFirst()
.map(d -> objectMapper.convertValue(d.getValue(), typeCache.apply(d.getType()))).orElse(null);
}
@Override
public T fromDocument(SerializedDocument document, Class type) {
return jsonNodeUpcaster.cast(Stream.of(inverter.fromDocument(document))).findFirst()
.map(d -> objectMapper.convertValue(d.getValue(), type)).orElse(null);
}
@Override
public V doConvert(Object value, Class type) {
return objectMapper.convertValue(value, type);
}
@Override
public Object doClone(Object value) {
return ReflectionUtils.copyFields(value, doConvert(objectMapper.createObjectNode(), value.getClass()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy