Skip to content

Commit

Permalink
Generalize IdAnnotations and IdDeSerializationModule
Browse files Browse the repository at this point in the history
  • Loading branch information
marecabo committed Oct 13, 2023
1 parent 7d802bb commit 248abc9
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 168 deletions.
182 changes: 60 additions & 122 deletions matsim/src/main/java/org/matsim/api/core/v01/IdAnnotations.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,177 +23,115 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Node;
import org.matsim.api.core.v01.population.Person;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

public interface IdAnnotations {

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = JsonPersonId.PersonIdSerializer.class)
@JsonDeserialize(using = JsonPersonId.PersonIdDeserializer.class)
public @interface JsonPersonId {

static class PersonIdSerializer extends StdSerializer<Id<Person>> {

protected PersonIdSerializer() {
this(null);
}
@JsonSerialize(using = JsonIdSerializer.class)
@JsonDeserialize(using = JsonIdContextualDeserializer.class)
public @interface JsonId {

protected PersonIdSerializer(Class<Id<Person>> vc) {
super(vc);
}
}

@Override
public void serialize(Id<Person> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.toString());
}
class JsonIdSerializer<T> extends StdSerializer<T> {

protected JsonIdSerializer() {
this(null);
}

static class PersonIdDeserializer extends StdDeserializer<Id<Person>> {

protected PersonIdDeserializer() {
this(null);
}

protected PersonIdDeserializer(Class<?> vc) {
super(vc);
}

@Override
public Id<Person> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return Id.createPersonId(node.asText());
}

protected JsonIdSerializer(Class<T> vc) {
super(vc);
}

static class PersonIdKeyDeserializer extends KeyDeserializer {

@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return Id.createPersonId(key);
}

@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.toString());
}

}

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = JsonLinkId.LinkIdSerializer.class)
@JsonDeserialize(using = JsonLinkId.LinkIdDeserializer.class)
public @interface JsonLinkId {

static class LinkIdSerializer extends StdSerializer<Id<Link>> {

protected LinkIdSerializer() {
this(null);
}

protected LinkIdSerializer(Class<Id<Link>> vc) {
super(vc);
}
class JsonIdContextualDeserializer<T> extends StdDeserializer<Id<T>> implements ContextualDeserializer {

@Override
public void serialize(Id<Link> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.toString());
}
private Class<T> idClass;

protected JsonIdContextualDeserializer() {
this(null);
}

static class LinkIdDeserializer extends StdDeserializer<Id<Link>> {

protected LinkIdDeserializer() {
this(null);
}
protected JsonIdContextualDeserializer(Class<T> idClass) {
super(Object.class);
this.idClass = idClass;
}

protected LinkIdDeserializer(Class<?> vc) {
super(vc);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
throws JsonMappingException {

@Override
public Id<Link> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return Id.createLinkId(node.asText());
final Class<? extends Object> idClass;
{
final JavaType type;
if (property != null)
type = property.getType();
else {
type = ctxt.getContextualType();
}
idClass = type.containedType(0).getRawClass();
}

return JsonIdDeserializer.getInstance(idClass);
}

static class LinkIdKeyDeserializer extends KeyDeserializer {

@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return Id.createLinkId(key);
}

@Override
public Id<T> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = jp.getCodec().readTree(jp);
return Id.create(node.asText(), idClass);
}

}

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = JsonNodeId.NodeIdSerializer.class)
@JsonDeserialize(using = JsonNodeId.NodeIdDeserializer.class)
public @interface JsonNodeId {

static class NodeIdSerializer extends StdSerializer<Id<Node>> {

protected NodeIdSerializer() {
this(null);
}

protected NodeIdSerializer(Class<Id<Node>> vc) {
super(vc);
}
class JsonIdDeserializer<T> extends StdDeserializer<Id<T>> {

@Override
public void serialize(Id<Node> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.toString());
}
private static final Map<Class<?>, JsonIdDeserializer<?>> CACHE = new HashMap<>();

public static JsonIdDeserializer<?> getInstance(Class<?> clazz) {
return CACHE.computeIfAbsent(clazz, k -> new JsonIdDeserializer<>(k));
}

static class NodeIdDeserializer extends StdDeserializer<Id<Node>> {

protected NodeIdDeserializer() {
this(null);
}

protected NodeIdDeserializer(Class<?> vc) {
super(vc);
}

@Override
public Id<Node> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return Id.createNodeId(node.asText());
}
private final Class<T> idClass;

private JsonIdDeserializer() {
this(null);
}

static class NodeIdKeyDeserializer extends KeyDeserializer {

@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return Id.createNodeId(key);
}
private JsonIdDeserializer(Class<T> idClass) {
super(Object.class);
this.idClass = idClass;
}

@Override
public Id<T> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return Id.create(node.asText(), idClass);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
package org.matsim.api.core.v01;

import java.util.Collections;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.tuple.Triple;
import org.matsim.api.core.v01.IdAnnotations.JsonLinkId;
import org.matsim.api.core.v01.IdAnnotations.JsonNodeId;
import org.matsim.api.core.v01.IdAnnotations.JsonPersonId;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Node;
import org.matsim.api.core.v01.population.Person;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
Expand All @@ -26,6 +19,7 @@
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.deser.KeyDeserializers;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

/**
* Use as follows with your {@link ObjectMapper} instance:
Expand All @@ -36,24 +30,6 @@ public class IdDeSerializationModule extends Module {

private static final String NAME = IdDeSerializationModule.class.getSimpleName();
private static final Version VERSION = new Version(0, 1, 0, null, "org.matsim", "api.core.v01");
private static final Map<Class<?>, Triple<JsonSerializer<?>, JsonDeserializer<?>, KeyDeserializer>> DE_SERIALIZER_MAP;
static {
Map<Class<?>, Triple<JsonSerializer<?>, JsonDeserializer<?>, KeyDeserializer>> m = new HashMap<>();
m.put(Person.class, Triple.of(
new JsonPersonId.PersonIdSerializer(),
new JsonPersonId.PersonIdDeserializer(),
new JsonPersonId.PersonIdKeyDeserializer()));
m.put(Node.class, Triple.of(
new JsonNodeId.NodeIdSerializer(),
new JsonNodeId.NodeIdDeserializer(),
new JsonNodeId.NodeIdKeyDeserializer()));
m.put(Link.class, Triple.of(
new JsonLinkId.LinkIdSerializer(),
new JsonLinkId.LinkIdDeserializer(),
new JsonLinkId.LinkIdKeyDeserializer()));
// Add your own classes below here
DE_SERIALIZER_MAP = Collections.unmodifiableMap(m);
}

private static Module instance = null;

Expand Down Expand Up @@ -85,13 +61,16 @@ public void setupModule(SetupContext context) {
context.addKeyDeserializers(new IdKeyDeserializers());
}

private static final StdSerializer<?> SERIALIZER = new IdAnnotations.JsonIdSerializer<>(Id.class);
private static final Map<Class<?>, KeyDeserializer> KEY_DESERIALIZER_CACHE = new HashMap<>();

private static final class IdSerializers extends Serializers.Base {

@Override
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type,
BeanDescription beanDesc) {
if (type.getRawClass().equals(Id.class) && type.containedTypeCount() == 1) {
return DE_SERIALIZER_MAP.get(type.containedType(0).getRawClass()).getLeft();
return SERIALIZER;
}
return null;
}
Expand All @@ -104,7 +83,7 @@ private static final class IdDeserializers extends Deserializers.Base {
public JsonDeserializer<?> findBeanDeserializer(JavaType type, DeserializationConfig config,
BeanDescription beanDesc) throws JsonMappingException {
if (type.getRawClass().equals(Id.class) && type.containedTypeCount() == 1) {
return DE_SERIALIZER_MAP.get(type.containedType(0).getRawClass()).getMiddle();
return IdAnnotations.JsonIdDeserializer.getInstance(type.containedType(0).getRawClass());
}
return null;
}
Expand All @@ -117,7 +96,15 @@ private static final class IdKeyDeserializers implements KeyDeserializers {
public KeyDeserializer findKeyDeserializer(JavaType type, DeserializationConfig config,
BeanDescription beanDesc) throws JsonMappingException {
if (type.getRawClass().equals(Id.class) && type.containedTypeCount() == 1) {
return DE_SERIALIZER_MAP.get(type.containedType(0).getRawClass()).getRight();
return KEY_DESERIALIZER_CACHE.computeIfAbsent(type.containedType(0).getRawClass(),
k -> new KeyDeserializer() {

@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return Id.create(key, k);
}

});
}
return null;
}
Expand Down
Loading

0 comments on commit 248abc9

Please sign in to comment.