Skip to content
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

Enable interval serialization #30

Merged
merged 15 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
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
8 changes: 3 additions & 5 deletions src/main/java/net/imagej/server/ImageJServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,20 @@
*
* @author Leon Yang
*/
public class ImageJServer extends
Application<ImageJServerConfiguration>
{
public class ImageJServer extends Application<ImageJServerConfiguration> {

private final Context ctx;

private final ObjectService objectService;

private final JsonService jsonService;

private Environment env;

public ImageJServer(final Context ctx) {
this.ctx = ctx;
objectService = new DefaultObjectService();
jsonService = new DefaultJsonService(objectService);
jsonService = new DefaultJsonService(ctx, objectService);
}

@Override
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/net/imagej/server/json/JsonSerializerAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.imagej.server.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

class JsonSerializerAdapter<T> extends StdSerializer<T> {

private SciJavaJsonSerializer<T> serializedDelegate;

public JsonSerializerAdapter(
SciJavaJsonSerializer<T> serializedDelegate)
{
super(serializedDelegate.handleType());
this.serializedDelegate = serializedDelegate;
}


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


}
32 changes: 32 additions & 0 deletions src/main/java/net/imagej/server/json/SciJavaJsonSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

package net.imagej.server.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;

import org.scijava.plugin.SciJavaPlugin;

public interface SciJavaJsonSerializer<T> extends SciJavaPlugin

{

void serialize(T value, JsonGenerator gen,
SerializerProvider serializers)
throws IOException;

Class<T> handleType();

default boolean isSupportedBy(Class<?> desiredClass) {
return handleType().isAssignableFrom(desiredClass);
}

default public void register(ObjectMapper mapper) {
SimpleModule mod = new SimpleModule();
mod.addSerializer(new JsonSerializerAdapter<>(this));
mapper.registerModule(mod);
}
}
38 changes: 33 additions & 5 deletions src/main/java/net/imagej/server/services/DefaultJsonService.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,15 @@

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import net.imagej.server.json.SciJavaJsonSerializer;
import net.imagej.server.mixins.Mixins;
import net.imglib2.EuclideanSpace;

import org.scijava.Context;
import org.scijava.plugin.PluginService;

/**
* Service that handle customized JSON serialization and deserialization.
*
Expand All @@ -73,13 +78,16 @@ public class DefaultJsonService implements JsonService {
*/
private final UntypedObjectDeserializer idToObjDeserializer;

private final List<?> jsonSerializers;

/**
* Constructs and initializes a JsonService with an {@link ObjectService}.
*
* @param objectService
*/
public DefaultJsonService(final ObjectService objectService) {

public DefaultJsonService(final Context ctx,
final ObjectService objectService)
{
idToObjDeserializer = new UntypedObjectDeserializer(null, null) {

@Override
Expand All @@ -101,7 +109,7 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt)

final JsonSerializer<Object> objToIdSerializer =
new JsonSerializer<Object>()
{
{

@Override
public void serialize(Object value, JsonGenerator gen,
Expand All @@ -120,20 +128,36 @@ public void serialize(Object value, JsonGenerator gen,
public JsonSerializer<?> modifySerializer(SerializationConfig config,
BeanDescription beanDesc, JsonSerializer<?> serializer)
{
if (Mixins.support(beanDesc.getBeanClass())) return serializer;
final Class<?> desiredClass = beanDesc.getBeanClass();

// If the serialized class is supported by mixins, let's go for one
if (Mixins.support(desiredClass)) return serializer;

// If the serialized class is supported thanks to a modification to
// ObjectMapper, let's do it that way
if (jsonSerializers.stream().map(obj -> (SciJavaJsonSerializer<?>) obj)
.anyMatch(e -> e.isSupportedBy(
desiredClass))) return serializer;

// If the serialized class is unknown (i.e. serialized using the general
// BeanSerializer) or should not be serialized (i.e. complicated class
// implemented interfaces such as Iterable), would be serialized as an
// ID.
if (serializer instanceof BeanSerializer) return objToIdSerializer;
if (notSerialized(beanDesc.getBeanClass())) return objToIdSerializer;
if (notSerialized(desiredClass)) return objToIdSerializer;

return serializer;

}
});
objToIdMapper = new ObjectMapper();
objToIdMapper.registerModule(objToIdModule);

jsonSerializers = ctx.getService(PluginService.class).createInstancesOfType(
SciJavaJsonSerializer.class);

registerSerializers();

// register Jackson MixIns to obtain better json output format for some
// specific types
Mixins.registerMixIns(objToIdMapper);
Expand All @@ -156,4 +180,8 @@ public boolean notSerialized(final Class<?> target) {
.isAssignableFrom(target));
}

private void registerSerializers() {
jsonSerializers.stream().map(obj -> (SciJavaJsonSerializer<?>) obj).forEach(
serializer -> serializer.register(objToIdMapper));
}
}
2 changes: 1 addition & 1 deletion src/test/java/net/imagej/server/AbstractResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public abstract class AbstractResourceTest {
protected static final ObjectService objectService =
new DefaultObjectService();

protected static final JsonService jsonService = new DefaultJsonService(
protected static final JsonService jsonService = new DefaultJsonService(ctx,
objectService);

protected static final ObjectMapper objectMapper = new ObjectMapper();
Expand Down
4 changes: 3 additions & 1 deletion src/test/java/net/imagej/server/DefaultJsonServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@

import org.junit.Before;
import org.junit.Test;
import org.scijava.Context;

/**
* Test deserialization and serialization using DefaultJsonService.
Expand All @@ -66,12 +67,13 @@ public class DefaultJsonServiceTest {
private ObjectMapper modifiedMapper;
private ListObjectService objectService;
private DefaultJsonService jsonService;
protected static final Context ctx = new Context();

@Before
public void setup() {
modifiedMapper = Jackson.newObjectMapper();
objectService = new ListObjectService();
jsonService = new DefaultJsonService(objectService);
jsonService = new DefaultJsonService(ctx, objectService);
jsonService.addDeserializerTo(modifiedMapper);
}

Expand Down
89 changes: 89 additions & 0 deletions src/test/java/net/imagej/server/SciJavaJsonSerializerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@

package net.imagej.server;

import static io.dropwizard.testing.FixtureHelpers.fixture;
import static org.junit.Assert.assertEquals;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;

import io.dropwizard.jackson.Jackson;

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

import net.imagej.server.json.SciJavaJsonSerializer;
import net.imagej.server.services.DefaultJsonService;
import net.imagej.server.services.DefaultObjectService;
import net.imglib2.FinalInterval;
import net.imglib2.Interval;

import org.junit.Test;
import org.scijava.Context;
import org.scijava.plugin.Plugin;

public class SciJavaJsonSerializerTest {

@Test
public void serializeIntervalUsingSciJavaJsonSerializer() throws Exception {

final ObjectMapper mapper = Jackson.newObjectMapper();
final DefaultObjectService objectService = new DefaultObjectService();
final DefaultJsonService jsonService = new DefaultJsonService(new Context(),
objectService);
jsonService.addDeserializerTo(mapper);

final Interval testInterval = new FinalInterval(new long[] { 0, 1 },
new long[] { 8, 9 });
final Map<String, Object> outputs = new HashMap<>();
outputs.put("testInterval", testInterval);

final String parsed = jsonService.parseObject(outputs);

final String expected = mapper.writeValueAsString(mapper.readValue(fixture(
"fixtures/outputs/finalIntervalType.json"), Map.class));

assertEquals(expected, parsed);

}

@Plugin(type = SciJavaJsonSerializer.class)
public static class ExampleIntervalJsonSerializer implements
SciJavaJsonSerializer<Interval>
{

@Override
public boolean isSupportedBy(Class<?> desiredClass) {
return desiredClass.equals(FinalInterval.class);
}

@Override
public void serialize(Interval interval, JsonGenerator gen,
SerializerProvider serializers) throws IOException
{

gen.writeStartObject();
gen.writeArrayFieldStart("min");
for (int i = 0; i < interval.numDimensions(); i++) {
gen.writeNumber(interval.min(i));
}
gen.writeEndArray();
gen.writeArrayFieldStart("max");
for (int i = 0; i < interval.numDimensions(); i++) {
gen.writeNumber(interval.max(i));
}
gen.writeEndArray();
gen.writeEndObject();

}

@Override
public Class<Interval> handleType() {
return Interval.class;
}

}

}
6 changes: 6 additions & 0 deletions src/test/resources/fixtures/outputs/finalIntervalType.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"testInterval": {
"min": [0, 1],
"max": [8, 9]
}
}