TINKERPOP-1274: GraphSON 2.0.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/b44ec666 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/b44ec666 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/b44ec666 Branch: refs/heads/TINKERPOP-1274 Commit: b44ec666f846d9acc7b0223887cb0b5e2e0789bd Parents: 0554b59 Author: Kevin Gallardo <kevin.galla...@datastax.com> Authored: Tue Jun 28 19:38:32 2016 +0100 Committer: Kevin Gallardo <kevin.galla...@datastax.com> Committed: Thu Jun 30 10:39:47 2016 +0100 ---------------------------------------------------------------------- .../structure/io/graphson/GraphSONIo.java | 6 +- .../structure/io/graphson/GraphSONMapper.java | 78 ++- .../structure/io/graphson/GraphSONModule.java | 106 ++--- .../structure/io/graphson/GraphSONReader.java | 23 +- .../io/graphson/GraphSONSerializerProvider.java | 28 +- .../io/graphson/GraphSONSerializersV1d0.java | 16 +- .../io/graphson/GraphSONSerializersV2d0.java | 467 ++++++++++++++++++ .../io/graphson/GraphSONTypeDeserializer.java | 201 ++++++++ .../io/graphson/GraphSONTypeIdResolver.java | 134 ++++++ .../graphson/GraphSONTypeResolverBuilder.java | 60 +++ .../io/graphson/GraphSONTypeSerializer.java | 160 +++++++ .../structure/io/graphson/GraphSONUtil.java | 29 ++ .../structure/io/graphson/GraphSONWriter.java | 12 +- .../io/graphson/JavaTimeSerializersV1d0.java | 14 +- .../io/graphson/JavaTimeSerializersV2d0.java | 311 ++++++++++++ .../io/graphson/JavaUtilSerializersV2d0.java | 116 +++++ .../structure/io/graphson/JsonParserConcat.java | 81 ++++ .../io/graphson/LegacyGraphSONReader.java | 14 +- .../io/graphson/TinkerPopJacksonModule.java | 59 +++ .../io/graphson/ToStringGraphSONSerializer.java | 41 ++ .../util/star/DirectionalStarGraph.java | 39 ++ .../star/StarGraphGraphSONDeserializer.java | 91 ++++ .../util/star/StarGraphGraphSONSerializer.java | 250 ---------- .../star/StarGraphGraphSONSerializerV1d0.java | 167 +++++++ .../star/StarGraphGraphSONSerializerV2d0.java | 158 +++++++ ...aphSONMapperV2d0PartialEmbeddedTypeTest.java | 292 ++++++++++++ .../io/graphson/GraphSONMapperV2d0Test.java | 171 +++++++ .../AbstractGraphSONMessageSerializerV2d0.java | 248 ++++++++++ .../GraphSONMessageSerializerGremlinV2d0.java | 68 +++ .../ser/GraphSONMessageSerializerV2d0.java | 124 +++++ ...raphSONMessageSerializerGremlinV2d0Test.java | 321 +++++++++++++ .../ser/GraphSONMessageSerializerV2d0Test.java | 474 +++++++++++++++++++ .../structure/TinkerIoRegistryV2d0.java | 227 +++++++++ .../structure/IoDataGenerationTest.java | 109 ++++- .../TinkerGraphGraphSONSerializerV2d0Test.java | 156 ++++++ 35 files changed, 4444 insertions(+), 407 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONIo.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONIo.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONIo.java index 0354a11..31cc6f2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONIo.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONIo.java @@ -22,11 +22,7 @@ import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.io.Io; import org.apache.tinkerpop.gremlin.structure.io.IoRegistry; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; /** * Constructs GraphSON IO implementations given a {@link Graph} and {@link IoRegistry}. Implementers of the {@link Graph} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java index be5377a..8f20ef7 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java @@ -33,6 +33,7 @@ import org.javatuples.Pair; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * An extension to the standard Jackson {@code ObjectMapper} which automatically registers the standard @@ -60,14 +61,17 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { private final boolean normalize; private final boolean embedTypes; private final GraphSONVersion version; + private final TypeInfo typeInfo; private GraphSONMapper(final List<SimpleModule> customModules, final boolean loadCustomSerializers, - final boolean normalize, final boolean embedTypes, final GraphSONVersion version) { + final boolean normalize, final boolean embedTypes, final GraphSONVersion version, + final TypeInfo typeInfo) { this.customModules = customModules; this.loadCustomSerializers = loadCustomSerializers; this.normalize = normalize; this.embedTypes = embedTypes; this.version = version; + this.typeInfo = typeInfo; } @Override @@ -75,6 +79,16 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { final ObjectMapper om = new ObjectMapper(); om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + GraphSONModule graphSONModule = version.getBuilder().create(normalize); + om.registerModule(graphSONModule); + customModules.forEach(om::registerModule); + + + // plugin external serialization modules + if (loadCustomSerializers) + om.findAndRegisterModules(); + + if (version == GraphSONVersion.V1_0) { if (embedTypes) { final TypeResolverBuilder<?> typer = new StdTypeResolverBuilder() @@ -84,29 +98,41 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { om.setDefaultTyping(typer); } } else if (version == GraphSONVersion.V2_0) { + if (typeInfo != TypeInfo.NO_TYPES) { + GraphSONTypeIdResolver graphSONTypeIdResolver = new GraphSONTypeIdResolver(); + final TypeResolverBuilder typer = new GraphSONTypeResolverBuilder() + .typesEmbedding(getTypeInfo()) + .init(JsonTypeInfo.Id.CUSTOM, graphSONTypeIdResolver) + .typeProperty(GraphSONTokens.CLASS); + // Register types to typeResolver for the GraphSON module + for (Map.Entry<String, Class> typeDeser : graphSONModule.getAddedDeserializers().entrySet()) { + graphSONTypeIdResolver.addCustomType(typeDeser.getKey(), typeDeser.getValue()); + } + + // Register types to typeResolver for the Custom modules + customModules.forEach(e -> { + if (e instanceof TinkerPopJacksonModule) { + for (Map.Entry<String, Class> typeDeser : ((TinkerPopJacksonModule) e).getAddedDeserializers().entrySet()) { + graphSONTypeIdResolver.addCustomType(typeDeser.getKey(), typeDeser.getValue()); + } + } + }); + om.setDefaultTyping(typer); + } } else { - throw new IllegalStateException("Unknown GraphSONVersion"); + throw new IllegalStateException("Unknown GraphSONVersion : " + version); } - if (normalize) - om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); - // this provider toStrings all unknown classes and converts keys in Map objects that are Object to String. - final DefaultSerializerProvider provider = new GraphSONSerializerProvider(); - provider.setDefaultKeySerializer(new GraphSONSerializersV1d0.GraphSONKeySerializer()); + final DefaultSerializerProvider provider = new GraphSONSerializerProvider(version); om.setSerializerProvider(provider); - om.registerModule(version.getBuilder().create(normalize)); - customModules.forEach(om::registerModule); - - // plugin external serialization modules - if (loadCustomSerializers) - om.findAndRegisterModules(); + if (normalize) + om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); // keep streams open to accept multiple values (e.g. multiple vertices) om.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - return om; } @@ -118,6 +144,15 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { return new Builder(); } + public TypeInfo getTypeInfo() { + return this.typeInfo; + } + + public enum TypeInfo { + NO_TYPES, + PARTIAL_TYPES + } + public static class Builder implements Mapper.Builder<Builder> { private List<SimpleModule> customModules = new ArrayList<>(); private boolean loadCustomModules = false; @@ -125,6 +160,8 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { private boolean embedTypes = false; private List<IoRegistry> registries = new ArrayList<>(); private GraphSONVersion version = GraphSONVersion.V1_0; + // GraphSON 2.0 should have types activated by default, otherwise use there's no point in using it instead of 1.0. + private TypeInfo typeInfo = TypeInfo.PARTIAL_TYPES; private Builder() { } @@ -187,13 +224,24 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { return this; } + /** + * Specify if the values are going to be typed or not, and at which level. + * + * The level can be NO_TYPES or PARTIAL_TYPES, and could be extended in the future. + */ + public Builder typeInfo(final TypeInfo typeInfo) { + this.typeInfo = typeInfo; + return this; + } + public GraphSONMapper create() { registries.forEach(registry -> { final List<Pair<Class, SimpleModule>> simpleModules = registry.find(GraphSONIo.class, SimpleModule.class); simpleModules.stream().map(Pair::getValue1).forEach(this.customModules::add); }); - return new GraphSONMapper(customModules, loadCustomModules, normalize, embedTypes, version); + return new GraphSONMapper(customModules, loadCustomModules, normalize, embedTypes, version, typeInfo); } + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java index 639e90d..06630f4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java @@ -19,30 +19,20 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson; import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializer; -import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; - -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.MonthDay; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.Period; -import java.time.Year; -import java.time.YearMonth; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; +import org.apache.tinkerpop.gremlin.structure.util.star.DirectionalStarGraph; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV1d0; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV2d0; + +import java.nio.ByteBuffer; +import java.time.*; import java.util.Map; -import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; /** * The set of serializers that handle the core graph interfaces. These serializers support normalization which @@ -54,7 +44,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; * * @author Stephen Mallette (http://stephen.genoprime.com) */ -abstract class GraphSONModule extends SimpleModule { +abstract class GraphSONModule extends TinkerPopJacksonModule { GraphSONModule(final String name) { super(name); @@ -64,54 +54,55 @@ abstract class GraphSONModule extends SimpleModule { * Version 2.0 of GraphSON. */ static final class GraphSONModuleV2d0 extends GraphSONModule { - /** * Constructs a new object. */ protected GraphSONModuleV2d0(final boolean normalize) { super("graphson-2.0"); + // graph - addSerializer(Edge.class, new GraphSONSerializersV1d0.EdgeJacksonSerializer(normalize)); - addSerializer(Vertex.class, new GraphSONSerializersV1d0.VertexJacksonSerializer(normalize)); - addSerializer(VertexProperty.class, new GraphSONSerializersV1d0.VertexPropertyJacksonSerializer(normalize)); - addSerializer(Property.class, new GraphSONSerializersV1d0.PropertyJacksonSerializer()); - addSerializer(TraversalMetrics.class, new GraphSONSerializersV1d0.TraversalMetricsJacksonSerializer()); - addSerializer(TraversalExplanation.class, new GraphSONSerializersV1d0.TraversalExplanationJacksonSerializer()); - addSerializer(Path.class, new GraphSONSerializersV1d0.PathJacksonSerializer()); - addSerializer(StarGraphGraphSONSerializer.DirectionalStarGraph.class, new StarGraphGraphSONSerializer(normalize)); - addSerializer(Tree.class, new GraphSONSerializersV1d0.TreeJacksonSerializer()); + addSerializer(Edge.class, new GraphSONSerializersV2d0.EdgeJacksonSerializer(normalize)); + addSerializer(Vertex.class, new GraphSONSerializersV2d0.VertexJacksonSerializer(normalize)); + addSerializer(VertexProperty.class, new GraphSONSerializersV2d0.VertexPropertyJacksonSerializer(normalize)); + addSerializer(Property.class, new GraphSONSerializersV2d0.PropertyJacksonSerializer()); + addSerializer(TraversalMetrics.class, new GraphSONSerializersV2d0.TraversalMetricsJacksonSerializer()); + addSerializer(TraversalExplanation.class, new GraphSONSerializersV2d0.TraversalExplanationJacksonSerializer()); + addSerializer(Path.class, new GraphSONSerializersV2d0.PathJacksonSerializer()); + addSerializer(DirectionalStarGraph.class, new StarGraphGraphSONSerializerV2d0(normalize)); + addSerializer(Tree.class, new GraphSONSerializersV2d0.TreeJacksonSerializer()); // java.util - addSerializer(Map.Entry.class, new JavaUtilSerializersV1d0.MapEntryJacksonSerializer()); + addSerializer(Map.Entry.class, new JavaUtilSerializersV2d0.MapEntryJacksonSerializer()); + addSerializer(ByteBuffer.class, new JavaUtilSerializersV2d0.GraphSONByteBufferSerializer()); // java.time - addSerializer(Duration.class, new JavaTimeSerializersV1d0.DurationJacksonSerializer()); - addSerializer(Instant.class, new JavaTimeSerializersV1d0.InstantJacksonSerializer()); - addSerializer(LocalDate.class, new JavaTimeSerializersV1d0.LocalDateJacksonSerializer()); - addSerializer(LocalDateTime.class, new JavaTimeSerializersV1d0.LocalDateTimeJacksonSerializer()); - addSerializer(LocalTime.class, new JavaTimeSerializersV1d0.LocalTimeJacksonSerializer()); - addSerializer(MonthDay.class, new JavaTimeSerializersV1d0.MonthDayJacksonSerializer()); - addSerializer(OffsetDateTime.class, new JavaTimeSerializersV1d0.OffsetDateTimeJacksonSerializer()); - addSerializer(OffsetTime.class, new JavaTimeSerializersV1d0.OffsetTimeJacksonSerializer()); - addSerializer(Period.class, new JavaTimeSerializersV1d0.PeriodJacksonSerializer()); - addSerializer(Year.class, new JavaTimeSerializersV1d0.YearJacksonSerializer()); - addSerializer(YearMonth.class, new JavaTimeSerializersV1d0.YearMonthJacksonSerializer()); - addSerializer(ZonedDateTime.class, new JavaTimeSerializersV1d0.ZonedDateTimeJacksonSerializer()); - addSerializer(ZoneOffset.class, new JavaTimeSerializersV1d0.ZoneOffsetJacksonSerializer()); - - addDeserializer(Duration.class, new JavaTimeSerializersV1d0.DurationJacksonDeserializer()); - addDeserializer(Instant.class, new JavaTimeSerializersV1d0.InstantJacksonDeserializer()); - addDeserializer(LocalDate.class, new JavaTimeSerializersV1d0.LocalDateJacksonDeserializer()); - addDeserializer(LocalDateTime.class, new JavaTimeSerializersV1d0.LocalDateTimeJacksonDeserializer()); - addDeserializer(LocalTime.class, new JavaTimeSerializersV1d0.LocalTimeJacksonDeserializer()); - addDeserializer(MonthDay.class, new JavaTimeSerializersV1d0.MonthDayJacksonDeserializer()); - addDeserializer(OffsetDateTime.class, new JavaTimeSerializersV1d0.OffsetDateTimeJacksonDeserializer()); - addDeserializer(OffsetTime.class, new JavaTimeSerializersV1d0.OffsetTimeJacksonDeserializer()); - addDeserializer(Period.class, new JavaTimeSerializersV1d0.PeriodJacksonDeserializer()); - addDeserializer(Year.class, new JavaTimeSerializersV1d0.YearJacksonDeserializer()); - addDeserializer(YearMonth.class, new JavaTimeSerializersV1d0.YearMonthJacksonDeserializer()); - addDeserializer(ZonedDateTime.class, new JavaTimeSerializersV1d0.ZonedDateTimeJacksonDeserializer()); - addDeserializer(ZoneOffset.class, new JavaTimeSerializersV1d0.ZoneOffsetJacksonDeserializer()); + addSerializer(Duration.class, new JavaTimeSerializersV2d0.DurationJacksonSerializer()); + addSerializer(Instant.class, new JavaTimeSerializersV2d0.InstantJacksonSerializer()); + addSerializer(LocalDate.class, new JavaTimeSerializersV2d0.LocalDateJacksonSerializer()); + addSerializer(LocalDateTime.class, new JavaTimeSerializersV2d0.LocalDateTimeJacksonSerializer()); + addSerializer(LocalTime.class, new JavaTimeSerializersV2d0.LocalTimeJacksonSerializer()); + addSerializer(MonthDay.class, new JavaTimeSerializersV2d0.MonthDayJacksonSerializer()); + addSerializer(OffsetDateTime.class, new JavaTimeSerializersV2d0.OffsetDateTimeJacksonSerializer()); + addSerializer(OffsetTime.class, new JavaTimeSerializersV2d0.OffsetTimeJacksonSerializer()); + addSerializer(Period.class, new JavaTimeSerializersV2d0.PeriodJacksonSerializer()); + addSerializer(Year.class, new JavaTimeSerializersV2d0.YearJacksonSerializer()); + addSerializer(YearMonth.class, new JavaTimeSerializersV2d0.YearMonthJacksonSerializer()); + addSerializer(ZonedDateTime.class, new JavaTimeSerializersV2d0.ZonedDateTimeJacksonSerializer()); + addSerializer(ZoneOffset.class, new JavaTimeSerializersV2d0.ZoneOffsetJacksonSerializer()); + + addDeserializer(Duration.class, new JavaTimeSerializersV2d0.DurationJacksonDeserializer()); + addDeserializer(Instant.class, new JavaTimeSerializersV2d0.InstantJacksonDeserializer()); + addDeserializer(LocalDate.class, new JavaTimeSerializersV2d0.LocalDateJacksonDeserializer()); + addDeserializer(LocalDateTime.class, new JavaTimeSerializersV2d0.LocalDateTimeJacksonDeserializer()); + addDeserializer(LocalTime.class, new JavaTimeSerializersV2d0.LocalTimeJacksonDeserializer()); + addDeserializer(MonthDay.class, new JavaTimeSerializersV2d0.MonthDayJacksonDeserializer()); + addDeserializer(OffsetDateTime.class, new JavaTimeSerializersV2d0.OffsetDateTimeJacksonDeserializer()); + addDeserializer(OffsetTime.class, new JavaTimeSerializersV2d0.OffsetTimeJacksonDeserializer()); + addDeserializer(Period.class, new JavaTimeSerializersV2d0.PeriodJacksonDeserializer()); + addDeserializer(Year.class, new JavaTimeSerializersV2d0.YearJacksonDeserializer()); + addDeserializer(YearMonth.class, new JavaTimeSerializersV2d0.YearMonthJacksonDeserializer()); + addDeserializer(ZonedDateTime.class, new JavaTimeSerializersV2d0.ZonedDateTimeJacksonDeserializer()); + addDeserializer(ZoneOffset.class, new JavaTimeSerializersV2d0.ZoneOffsetJacksonDeserializer()); } public static Builder build() { @@ -126,6 +117,7 @@ abstract class GraphSONModule extends SimpleModule { public GraphSONModule create(final boolean normalize) { return new GraphSONModuleV2d0(normalize); } + } } @@ -147,7 +139,7 @@ abstract class GraphSONModule extends SimpleModule { addSerializer(TraversalMetrics.class, new GraphSONSerializersV1d0.TraversalMetricsJacksonSerializer()); addSerializer(TraversalExplanation.class, new GraphSONSerializersV1d0.TraversalExplanationJacksonSerializer()); addSerializer(Path.class, new GraphSONSerializersV1d0.PathJacksonSerializer()); - addSerializer(StarGraphGraphSONSerializer.DirectionalStarGraph.class, new StarGraphGraphSONSerializer(normalize)); + addSerializer(DirectionalStarGraph.class, new StarGraphGraphSONSerializerV1d0(normalize)); addSerializer(Tree.class, new GraphSONSerializersV1d0.TreeJacksonSerializer()); // java.util http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java index d95fdff..6f15979 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java @@ -18,13 +18,7 @@ */ package org.apache.tinkerpop.gremlin.structure.io.graphson; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.io.GraphReader; import org.apache.tinkerpop.gremlin.structure.io.GraphWriter; import org.apache.tinkerpop.gremlin.structure.io.Mapper; @@ -35,7 +29,7 @@ import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty; import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty; import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; -import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializer; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONDeserializer; import org.apache.tinkerpop.gremlin.util.function.FunctionUtils; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; @@ -44,12 +38,7 @@ import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; import org.apache.tinkerpop.shaded.jackson.databind.node.JsonNodeType; import org.javatuples.Pair; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; +import java.io.*; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -169,14 +158,14 @@ public final class GraphSONReader implements GraphReader { final Function<Attachable<Edge>, Edge> edgeAttachMethod, final Direction attachEdgesOfThisDirection) throws IOException { final Map<String, Object> vertexData = mapper.readValue(inputStream, mapTypeReference); - final StarGraph starGraph = StarGraphGraphSONSerializer.readStarGraphVertex(vertexData); + final StarGraph starGraph = StarGraphGraphSONDeserializer.readStarGraphVertex(vertexData); if (vertexAttachMethod != null) vertexAttachMethod.apply(starGraph.getStarVertex()); if (vertexData.containsKey(GraphSONTokens.OUT_E) && (attachEdgesOfThisDirection == Direction.BOTH || attachEdgesOfThisDirection == Direction.OUT)) - StarGraphGraphSONSerializer.readStarGraphEdges(edgeAttachMethod, starGraph, vertexData, GraphSONTokens.OUT_E); + StarGraphGraphSONDeserializer.readStarGraphEdges(edgeAttachMethod, starGraph, vertexData, GraphSONTokens.OUT_E); if (vertexData.containsKey(GraphSONTokens.IN_E) && (attachEdgesOfThisDirection == Direction.BOTH || attachEdgesOfThisDirection == Direction.IN)) - StarGraphGraphSONSerializer.readStarGraphEdges(edgeAttachMethod, starGraph, vertexData, GraphSONTokens.IN_E); + StarGraphGraphSONDeserializer.readStarGraphEdges(edgeAttachMethod, starGraph, vertexData, GraphSONTokens.IN_E); return starGraph.getStarVertex(); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java index ed20fd9..c4dcddd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java @@ -26,32 +26,48 @@ import org.apache.tinkerpop.shaded.jackson.databind.ser.SerializerFactory; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.ToStringSerializer; /** - * Implementation of the {@code DefaultSerializerProvider} for Jackson that users the {@code ToStringSerializer} for + * Implementation of the {@code DefaultSerializerProvider} for Jackson that uses the {@code ToStringSerializer} for * unknown types. * * @author Stephen Mallette (http://stephen.genoprime.com) */ final class GraphSONSerializerProvider extends DefaultSerializerProvider { private static final long serialVersionUID = 1L; - private static final ToStringSerializer toStringSerializer = new ToStringSerializer(); + private final JsonSerializer<Object> unknownTypeSerializer; - public GraphSONSerializerProvider() { + public GraphSONSerializerProvider(GraphSONVersion version) { super(); + if (version == GraphSONVersion.V1_0) { + setDefaultKeySerializer(new GraphSONSerializersV1d0.GraphSONKeySerializer()); + unknownTypeSerializer = new ToStringSerializer(); + } else { + setDefaultKeySerializer(new GraphSONSerializersV2d0.GraphSONKeySerializer()); + unknownTypeSerializer = new ToStringGraphSONSerializer(); + } } protected GraphSONSerializerProvider(final SerializerProvider src, - final SerializationConfig config, final SerializerFactory f) { + final SerializationConfig config, final SerializerFactory f, + final GraphSONVersion version) { super(src, config, f); + if (version == GraphSONVersion.V1_0) { + setDefaultKeySerializer(new GraphSONSerializersV1d0.GraphSONKeySerializer()); + unknownTypeSerializer = new ToStringSerializer(); + } else { + setDefaultKeySerializer(new GraphSONSerializersV2d0.GraphSONKeySerializer()); + unknownTypeSerializer = new ToStringGraphSONSerializer(); + } + } @Override public JsonSerializer<Object> getUnknownTypeSerializer(final Class<?> aClass) { - return toStringSerializer; + return unknownTypeSerializer; } @Override public GraphSONSerializerProvider createInstance(final SerializationConfig config, final SerializerFactory jsf) { - return new GraphSONSerializerProvider(this, config, jsf); + return new GraphSONSerializerProvider(this, config, jsf, GraphSONVersion.V1_0); } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1d0.java index 2411d8f..7e78222 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV1d0.java @@ -21,14 +21,11 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson; import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Element; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.Comparators; import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; @@ -42,15 +39,8 @@ import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; import org.javatuples.Pair; import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; -import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; /** * GraphSON serializers for graph-based objects such as vertices, edges, properties, and paths. These serializers http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java new file mode 100644 index 0000000..e4f8280 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java @@ -0,0 +1,467 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.util.Comparators; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerationException; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdKeySerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; +import org.javatuples.Pair; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil.*; + +/** + * GraphSON serializers for graph-based objects such as vertices, edges, properties, and paths. These serializers + * present a generalized way to serialize the implementations of core interfaces. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GraphSONSerializersV2d0 { + + private GraphSONSerializersV2d0() { + } + + final static class VertexPropertyJacksonSerializer extends StdSerializer<VertexProperty> { + + private final boolean normalize; + + public VertexPropertyJacksonSerializer(final boolean normalize) { + super(VertexProperty.class); + this.normalize = normalize; + } + + @Override + public void serialize(final VertexProperty property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + serializerVertexProperty(property, jsonGenerator, serializerProvider, null, normalize, true); + } + + @Override + public void serializeWithType(final VertexProperty property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + serializerVertexProperty(property, jsonGenerator, serializerProvider, typeSerializer, normalize, true); + } + } + + final static class PropertyJacksonSerializer extends StdSerializer<Property> { + + public PropertyJacksonSerializer() { + super(Property.class); + } + + @Override + public void serialize(final Property property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + ser(property, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final Property property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + ser(property, jsonGenerator, serializerProvider, typeSerializer); + } + + private static void ser(final Property property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + writeStartObject(property, jsonGenerator, typeSerializer); + + serializerProvider.defaultSerializeField(GraphSONTokens.KEY, property.key(), jsonGenerator); + serializerProvider.defaultSerializeField(GraphSONTokens.VALUE, property.value(), jsonGenerator); + + writeEndObject(property, jsonGenerator, typeSerializer); + } + } + + final static class EdgeJacksonSerializer extends StdSerializer<Edge> { + + private final boolean normalize; + + public EdgeJacksonSerializer(final boolean normalize) { + super(Edge.class); + this.normalize = normalize; + } + + + @Override + public void serialize(final Edge edge, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + ser(edge, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final Edge edge, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + ser(edge, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final Edge edge, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + writeStartObject(edge, jsonGenerator, typeSerializer); + + GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer); + jsonGenerator.writeStringField(GraphSONTokens.LABEL, edge.label()); + jsonGenerator.writeStringField(GraphSONTokens.TYPE, GraphSONTokens.EDGE); + jsonGenerator.writeStringField(GraphSONTokens.IN_LABEL, edge.inVertex().label()); + jsonGenerator.writeStringField(GraphSONTokens.OUT_LABEL, edge.outVertex().label()); + GraphSONUtil.writeWithType(GraphSONTokens.IN, edge.inVertex().id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.OUT, edge.outVertex().id(), jsonGenerator, serializerProvider, typeSerializer); + writeProperties(edge, jsonGenerator, serializerProvider, typeSerializer); + + writeEndObject(edge, jsonGenerator, typeSerializer); + } + + private void writeProperties(final Edge edge, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException { + final Iterator<Property<Object>> elementProperties = normalize ? + IteratorUtils.list(edge.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : edge.properties(); + if (elementProperties.hasNext()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + writeStartObject(edge, jsonGenerator, typeSerializer); + + while (elementProperties.hasNext()) { + final Property<Object> elementProperty = elementProperties.next(); + GraphSONUtil.writeWithType(elementProperty.key(), elementProperty.value(), jsonGenerator, serializerProvider, typeSerializer); + } + + writeEndObject(edge, jsonGenerator, typeSerializer); + } + } + + } + + final static class VertexJacksonSerializer extends StdSerializer<Vertex> { + + private final boolean normalize; + + public VertexJacksonSerializer(final boolean normalize) { + super(Vertex.class); + this.normalize = normalize; + } + + @Override + public void serialize(final Vertex vertex, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + ser(vertex, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final Vertex vertex, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + ser(vertex, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final Vertex vertex, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) + throws IOException { + writeStartObject(vertex, jsonGenerator, typeSerializer); + + GraphSONUtil.writeWithType(GraphSONTokens.ID, vertex.id(), jsonGenerator, serializerProvider, typeSerializer); + jsonGenerator.writeStringField(GraphSONTokens.LABEL, vertex.label()); + jsonGenerator.writeStringField(GraphSONTokens.TYPE, GraphSONTokens.VERTEX); + writeProperties(vertex, jsonGenerator, serializerProvider, typeSerializer); + + writeEndObject(vertex, jsonGenerator, typeSerializer); + } + + private void writeProperties(final Vertex vertex, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + writeStartObject(vertex, jsonGenerator, typeSerializer); + + final List<String> keys = normalize ? + IteratorUtils.list(vertex.keys().iterator(), Comparator.naturalOrder()) : new ArrayList<>(vertex.keys()); + for (String key : keys) { + final Iterator<VertexProperty<Object>> vertexProperties = normalize ? + IteratorUtils.list(vertex.properties(key), Comparators.PROPERTY_COMPARATOR).iterator() : vertex.properties(key); + if (vertexProperties.hasNext()) { + jsonGenerator.writeFieldName(key); + writeStartArray(vertex, jsonGenerator, typeSerializer); + + while (vertexProperties.hasNext()) { + serializerVertexProperty(vertexProperties.next(), jsonGenerator, serializerProvider, typeSerializer, normalize, false); + } + + writeEndArray(vertex, jsonGenerator, typeSerializer); + } + } + + writeEndObject(vertex, jsonGenerator, typeSerializer); + } + + } + + final static class PathJacksonSerializer extends StdSerializer<Path> { + + public PathJacksonSerializer() { + super(Path.class); + } + + @Override + public void serialize(final Path path, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException, JsonGenerationException { + ser(path, jsonGenerator, null); + } + + @Override + public void serializeWithType(final Path path, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) + throws IOException, JsonProcessingException { + ser(path, jsonGenerator, typeSerializer); + } + + private static void ser(final Path path, final JsonGenerator jsonGenerator, final TypeSerializer typeSerializer) + throws IOException { + writeStartObject(path, jsonGenerator, typeSerializer); + + jsonGenerator.writeObjectField(GraphSONTokens.LABELS, path.labels()); + jsonGenerator.writeObjectField(GraphSONTokens.OBJECTS, path.objects()); + + writeEndObject(path, jsonGenerator, typeSerializer); + } + + } + + final static class TreeJacksonSerializer extends StdSerializer<Tree> { + + public TreeJacksonSerializer() { + super(Tree.class); + } + + @Override + public void serialize(final Tree tree, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { + ser(tree, jsonGenerator, null); + } + + @Override + public void serializeWithType(final Tree tree, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) + throws IOException, JsonProcessingException { + ser(tree, jsonGenerator, typeSerializer); + } + + private static void ser(final Tree tree, final JsonGenerator jsonGenerator, final TypeSerializer typeSerializer) + throws IOException { + writeStartObject(tree, jsonGenerator, typeSerializer); + + Set<Map.Entry<Element, Tree>> set = tree.entrySet(); + for (Map.Entry<Element, Tree> entry : set) { + jsonGenerator.writeFieldName(entry.getKey().id().toString()); + writeStartObject(entry, jsonGenerator, typeSerializer); + jsonGenerator.writeObjectField(GraphSONTokens.KEY, entry.getKey()); + jsonGenerator.writeObjectField(GraphSONTokens.VALUE, entry.getValue()); + writeEndObject(entry, jsonGenerator, typeSerializer); + } + + writeEndObject(tree, jsonGenerator, typeSerializer); + } + } + + /** + * Maps in the JVM can have {@link Object} as a key, but in JSON they must be a {@link String}. + */ + final static class GraphSONKeySerializer extends StdKeySerializer { + + @Override + public void serialize(final Object o, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException { + ser(o, jsonGenerator, serializerProvider); + } + + @Override + public void serializeWithType(final Object o, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + ser(o, jsonGenerator, serializerProvider); + } + + private void ser(final Object o, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException { + if (Element.class.isAssignableFrom(o.getClass())) + jsonGenerator.writeFieldName((((Element) o).id()).toString()); + else + super.serialize(o, jsonGenerator, serializerProvider); + } + } + + final static class TraversalExplanationJacksonSerializer extends StdSerializer<TraversalExplanation> { + public TraversalExplanationJacksonSerializer() { + super(TraversalExplanation.class); + } + + @Override + public void serialize(final TraversalExplanation traversalExplanation, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException { + ser(traversalExplanation, jsonGenerator); + } + + @Override + public void serializeWithType(final TraversalExplanation value, final JsonGenerator gen, + final SerializerProvider serializers, final TypeSerializer typeSer) throws IOException { + ser(value, gen); + } + + public void ser(final TraversalExplanation te, final JsonGenerator jsonGenerator) throws IOException { + final Map<String, Object> m = new HashMap<>(); + m.put(GraphSONTokens.ORIGINAL, getStepsAsList(te.getOriginalTraversal())); + + final List<Pair<TraversalStrategy, Traversal.Admin<?, ?>>> strategyTraversals = te.getStrategyTraversals(); + + final List<Map<String, Object>> intermediates = new ArrayList<>(); + for (final Pair<TraversalStrategy, Traversal.Admin<?, ?>> pair : strategyTraversals) { + final Map<String, Object> intermediate = new HashMap<>(); + intermediate.put(GraphSONTokens.STRATEGY, pair.getValue0().toString()); + intermediate.put(GraphSONTokens.CATEGORY, pair.getValue0().getTraversalCategory().getSimpleName()); + intermediate.put(GraphSONTokens.TRAVERSAL, getStepsAsList(pair.getValue1())); + intermediates.add(intermediate); + } + m.put(GraphSONTokens.INTERMEDIATE, intermediates); + + if (strategyTraversals.isEmpty()) + m.put(GraphSONTokens.FINAL, getStepsAsList(te.getOriginalTraversal())); + else + m.put(GraphSONTokens.FINAL, getStepsAsList(strategyTraversals.get(strategyTraversals.size() - 1).getValue1())); + + jsonGenerator.writeObject(m); + } + + private List<String> getStepsAsList(final Traversal.Admin<?, ?> t) { + final List<String> steps = new ArrayList<>(); + t.getSteps().iterator().forEachRemaining(s -> steps.add(s.toString())); + return steps; + } + } + + final static class TraversalMetricsJacksonSerializer extends StdSerializer<TraversalMetrics> { + public TraversalMetricsJacksonSerializer() { + super(TraversalMetrics.class); + } + + @Override + public void serialize(final TraversalMetrics property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + serializeInternal(property, jsonGenerator); + } + + @Override + public void serializeWithType(final TraversalMetrics property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + serializeInternal(property, jsonGenerator); + } + + private static void serializeInternal(final TraversalMetrics traversalMetrics, final JsonGenerator jsonGenerator) throws IOException { + // creation of the map enables all the fields to be properly written with their type if required + final Map<String, Object> m = new HashMap<>(); + m.put(GraphSONTokens.DURATION, traversalMetrics.getDuration(TimeUnit.NANOSECONDS) / 1000000d); + final List<Map<String, Object>> metrics = new ArrayList<>(); + traversalMetrics.getMetrics().forEach(it -> metrics.add(metricsToMap(it))); + m.put(GraphSONTokens.METRICS, metrics); + + jsonGenerator.writeObject(m); + } + + private static Map<String, Object> metricsToMap(final Metrics metrics) { + final Map<String, Object> m = new HashMap<>(); + m.put(GraphSONTokens.ID, metrics.getId()); + m.put(GraphSONTokens.NAME, metrics.getName()); + m.put(GraphSONTokens.COUNTS, metrics.getCounts()); + m.put(GraphSONTokens.DURATION, metrics.getDuration(TimeUnit.NANOSECONDS) / 1000000d); + + if (!metrics.getAnnotations().isEmpty()) { + m.put(GraphSONTokens.ANNOTATIONS, metrics.getAnnotations()); + } + + if (!metrics.getNested().isEmpty()) { + final List<Map<String, Object>> nested = new ArrayList<>(); + metrics.getNested().forEach(it -> nested.add(metricsToMap(it))); + m.put(GraphSONTokens.METRICS, nested); + } + return m; + } + } + + private static void serializerVertexProperty(final VertexProperty property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, final boolean normalize, + final boolean includeLabel) throws IOException { + writeStartObject(property, jsonGenerator, typeSerializer); + + GraphSONUtil.writeWithType(GraphSONTokens.ID, property.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.VALUE, property.value(), jsonGenerator, serializerProvider, typeSerializer); + if (includeLabel) jsonGenerator.writeStringField(GraphSONTokens.LABEL, property.label()); + tryWriteMetaProperties(property, jsonGenerator, serializerProvider, typeSerializer, normalize); + + writeEndObject(property, jsonGenerator, typeSerializer); + } + + private static void tryWriteMetaProperties(final VertexProperty property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, final boolean normalize) throws IOException { + // when "detached" you can't check features of the graph it detached from so it has to be + // treated differently from a regular VertexProperty implementation. + if (property instanceof DetachedVertexProperty) { + // only write meta properties key if they exist + if (property.properties().hasNext()) { + writeMetaProperties(property, jsonGenerator, serializerProvider, typeSerializer, normalize); + } + } else { + // still attached - so we can check the features to see if it's worth even trying to write the + // meta properties key + if (property.graph().features().vertex().supportsMetaProperties() && property.properties().hasNext()) { + writeMetaProperties(property, jsonGenerator, serializerProvider, typeSerializer, normalize); + } + } + } + + private static void writeMetaProperties(final VertexProperty property, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, final boolean normalize) throws IOException { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + writeStartObject(property, jsonGenerator, typeSerializer); + + final Iterator<Property<Object>> metaProperties = normalize ? + IteratorUtils.list((Iterator<Property<Object>>) property.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : property.properties(); + while (metaProperties.hasNext()) { + final Property<Object> metaProperty = metaProperties.next(); + GraphSONUtil.writeWithType(metaProperty.key(), metaProperty.value(), jsonGenerator, serializerProvider, typeSerializer); + } + + writeEndObject(property, jsonGenerator, typeSerializer); + } + +} + + + http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeDeserializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeDeserializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeDeserializer.java new file mode 100644 index 0000000..a7583fc --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeDeserializer.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper.TypeInfo; +import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.core.JsonToken; +import org.apache.tinkerpop.shaded.jackson.databind.BeanProperty; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.JsonDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.impl.TypeDeserializerBase; +import org.apache.tinkerpop.shaded.jackson.databind.type.TypeFactory; +import org.apache.tinkerpop.shaded.jackson.databind.util.TokenBuffer; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Contains main logic for the whole JSON to Java deserialization. Handles types embedded with the version 2.0 of GraphSON. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public class GraphSONTypeDeserializer extends TypeDeserializerBase { + private final TypeIdResolver idRes; + private final String propertyName; + private final JavaType baseType; + private final TypeInfo typeInfo; + + private static final JavaType mapJavaType = TypeFactory.defaultInstance().constructType(Map.class); + private static final JavaType arrayJavaType = TypeFactory.defaultInstance().constructType(List.class); + + + GraphSONTypeDeserializer(JavaType baseType, TypeIdResolver idRes, String typePropertyName, + TypeInfo typeInfo){ + super(baseType, idRes, typePropertyName, false, null); + this.baseType = baseType; + this.idRes = idRes; + this.propertyName = typePropertyName; + this.typeInfo = typeInfo; + } + + @Override + public TypeDeserializer forProperty(BeanProperty beanProperty) { + return this; + } + + @Override + public JsonTypeInfo.As getTypeInclusion() { + return JsonTypeInfo.As.WRAPPER_ARRAY; + } + + + @Override + public TypeIdResolver getTypeIdResolver() { + return idRes; + } + + @Override + public Class<?> getDefaultImpl() { + return null; + } + + @Override + public Object deserializeTypedFromObject(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + return deserialize(jsonParser, deserializationContext); + } + + @Override + public Object deserializeTypedFromArray(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + return deserialize(jsonParser, deserializationContext); + } + + @Override + public Object deserializeTypedFromScalar(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + return deserialize(jsonParser, deserializationContext); + } + + @Override + public Object deserializeTypedFromAny(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + return deserialize(jsonParser, deserializationContext); + } + + /** + * Main logic for the deserialization. + */ + private Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + TokenBuffer buf = new TokenBuffer(jsonParser.getCodec(), false); + + // Detect type + try { + // The Type pattern is START_ARRAY -> START_OBJECT -> TEXT_FIELD(propertyName). + if (jsonParser.getCurrentToken() == JsonToken.START_ARRAY) { + buf.writeStartArray(); + JsonToken t1 = jsonParser.nextToken(); + + if (t1 == JsonToken.START_OBJECT) { + buf.writeStartObject(); + String nextFieldName = jsonParser.nextFieldName(); + + if (nextFieldName != null) { + if (nextFieldName.equals(propertyName)) { + // Detected type pattern + // Find deserializer + // Deserialize with deserializer + String typeName = jsonParser.nextTextValue(); + JavaType typeFromId = idRes.typeFromId(typeName); + + // Detected a type and extracted the value, but still want to make sure that the extracted type + // corresponds to what was given in parameter, if a type has been explicitly specified in param. + if (!baseType.isJavaLangObject() && baseType != typeFromId) { + throw new InstantiationException( + String.format("Cannot deserialize the value with the detected type contained in the JSON (\"%s\") " + + "to the type specified in parameter to the object mapper (%s). " + + "Those types are incompatible.", typeName, baseType.getRawClass().toString()) + ); + } + + JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(typeFromId, null); + + // Position the next token right on the value + if (jsonParser.nextToken() == JsonToken.END_OBJECT) { + // Locate the cursor on the value to deser + jsonParser.nextValue(); + Object value = jsonDeserializer.deserialize(jsonParser, deserializationContext); + // IMPORTANT - Close the JSON ARRAY + jsonParser.nextToken(); + return value; + } + } + } + + } + } + } catch (Exception e) { + throw deserializationContext.mappingException("Could not deserialize the JSON value as required. " + e.getMessage()); + } + + JsonParser toUseParser; + JsonParser bufferParser = buf.asParser(); + JsonToken t = bufferParser.nextToken(); + + // While searching for the type pattern, we may have moved the cursor of the original JsonParser in param. + // To compensate, we have filled consistently a TokenBuffer that should contain the equivalent of + // what we skipped while searching for the pattern. + // This has an huge positive impact on performances, since JsonParser does not have a 'rewind()', + // the only other solution would have been to copy the whole original JsonParser. Which we avoid here and use + // an efficient structure made of TokenBuffer + JsonParserSequence/Concat. + if (t != null) + toUseParser = JsonParserConcat.createFlattened(bufferParser, jsonParser); + + // If the cursor hasn't been moved, no need to concatenate the original JsonParser with the TokenBuffer's one. + else + toUseParser = jsonParser; + + // If a type has been specified in parameter : + if (!baseType.isJavaLangObject()) { + JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(baseType, null); + return jsonDeserializer.deserialize(toUseParser, deserializationContext); + } + // Otherwise, detect the current structure : + else { + if (toUseParser.isExpectedStartArrayToken()) { + return deserializationContext.findContextualValueDeserializer(arrayJavaType, null).deserialize(toUseParser, deserializationContext); + } else if (toUseParser.isExpectedStartObjectToken()) { + return deserializationContext.findContextualValueDeserializer(mapJavaType, null).deserialize(toUseParser, deserializationContext); + } else { + // There's JavaLangObject in param, there's no type detected in the payload, the payload isn't a JSON Map or JSON List + // then consider it a simple type, even though we shouldn't be here if it was a simple type. + // TODO : maybe throw an error instead? + // throw deserializationContext.mappingException("Roger, we have a problem deserializing"); + JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(baseType, null); + return jsonDeserializer.deserialize(toUseParser, deserializationContext); + } + } + } + + private boolean canReadTypeId() { + return this.typeInfo == TypeInfo.PARTIAL_TYPES; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java new file mode 100644 index 0000000..97790ef --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo; +import org.apache.tinkerpop.shaded.jackson.databind.DatabindContext; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver; +import org.apache.tinkerpop.shaded.jackson.databind.type.TypeFactory; +import org.apache.tinkerpop.shaded.jackson.databind.util.TokenBuffer; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.sql.Timestamp; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Provides quick lookup for Type deserialization extracted from the JSON payload. As well as the Java Object to types + * compatible for the version 2.0 of GraphSON. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public class GraphSONTypeIdResolver implements TypeIdResolver { + + private final Map<String, JavaType> idToType = new HashMap<>(); + + GraphSONTypeIdResolver() { + // Need to add all the "Standard typed scalar" classes manually... + Arrays.asList( + Float.class, + Long.class, + Short.class, + BigInteger.class, + BigDecimal.class, + Byte.class, + Character.class, + UUID.class, + InetAddress.class, + InetSocketAddress.class, + ByteBuffer.class, + Class.class, + Calendar.class, + Date.class, + TimeZone.class, + Timestamp.class, + AtomicBoolean.class, + AtomicReference.class, + TokenBuffer.class + ).forEach(e -> idToType.put(e.getSimpleName(), TypeFactory.defaultInstance().constructType(e))); + } + + public Map<String, JavaType> getIdToType() { + return idToType; + } + + public GraphSONTypeIdResolver addCustomType(Class clasz) { + // May override types already registered, that's wanted. + getIdToType().put(clasz.getSimpleName(), TypeFactory.defaultInstance().constructType(clasz)); + return this; + } + + // Override manually a type definition. + public GraphSONTypeIdResolver addCustomType(String name, Class clasz) { + // May override types already registered, that's wanted. + getIdToType().put(name, TypeFactory.defaultInstance().constructType(clasz)); + return this; + } + + @Override + public void init(JavaType javaType) { + } + + @Override + public String idFromValue(Object o) { + return idFromValueAndType(o, null); + } + + @Override + public String idFromValueAndType(Object o, Class<?> aClass) { + // May be improved later + return o.getClass().getSimpleName(); + } + + @Override + public String idFromBaseType() { + return null; + } + + @Override + public JavaType typeFromId(String s) { + return typeFromId(null, s); + } + + @Override + public JavaType typeFromId(DatabindContext databindContext, String s) { + // Get the type from the string from the stored Map. If not found, default to deserialize as a String. + return getIdToType().get(s) != null + ? getIdToType().get(s) + // TODO: shouldn't we fail instead, if the type is not found? Or log something? + : TypeFactory.defaultInstance().constructType(String.class); + } + + @Override + public String getDescForKnownTypeIds() { + // TODO (?) + return "GraphSON advanced typing system"; + } + + @Override + public JsonTypeInfo.Id getMechanism() { + return JsonTypeInfo.Id.CUSTOM; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java new file mode 100644 index 0000000..87ab3a3 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper.TypeInfo; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationConfig; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.SerializationConfig; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.NamedType; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.impl.StdTypeResolverBuilder; + +import java.util.Collection; + +/** + * Creates the Type serializers as well as the Typed deserializers that will be provided to the serializers and + * deserializers. Contains the typeInfo level that should be provided by the GraphSONMapper. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public class GraphSONTypeResolverBuilder extends StdTypeResolverBuilder { + + private TypeInfo typeInfo; + + @Override + public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) { + TypeIdResolver idRes = this.idResolver(config, baseType, subtypes, false, true); + return new GraphSONTypeDeserializer(baseType, idRes, this.getTypeProperty(), typeInfo); + } + + + @Override + public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes) { + TypeIdResolver idRes = this.idResolver(config, baseType, subtypes, true, false); + return new GraphSONTypeSerializer(idRes, this.getTypeProperty(), typeInfo); + } + + public StdTypeResolverBuilder typesEmbedding(TypeInfo typeInfo) { + this.typeInfo = typeInfo; + return this; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java new file mode 100644 index 0000000..5511b10 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper.TypeInfo; +import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.databind.BeanProperty; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; + +import java.io.IOException; + +/** + * Extension of the Jackson's default TypeSerializer. An instance of this object will be passed to the serializers + * on which they can safely call the utility methods to serialize types and making it compatible with the version + * 2.0 of GraphSON. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public class GraphSONTypeSerializer extends TypeSerializer { + + private final TypeIdResolver idRes; + private final String propertyName; + private final TypeInfo typeInfo; + + GraphSONTypeSerializer(TypeIdResolver idRes, String propertyName, TypeInfo typeInfo) { + this.idRes = idRes; + this.propertyName = propertyName; + this.typeInfo = typeInfo; + } + + @Override + public TypeSerializer forProperty(BeanProperty beanProperty) { + return this; + } + + @Override + public JsonTypeInfo.As getTypeInclusion() { + return null; + } + + @Override + public String getPropertyName() { + return propertyName; + } + + @Override + public TypeIdResolver getTypeIdResolver() { + return idRes; + } + + @Override + public void writeTypePrefixForScalar(Object o, JsonGenerator jsonGenerator) throws IOException { + if (canWriteTypeId()) { + writeTypePrefix(jsonGenerator, getTypeIdResolver().idFromValue(o)); + } + } + + @Override + public void writeTypePrefixForObject(Object o, JsonGenerator jsonGenerator) throws IOException { + jsonGenerator.writeStartObject(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypePrefix(Map); + } + + @Override + public void writeTypePrefixForArray(Object o, JsonGenerator jsonGenerator) throws IOException { + jsonGenerator.writeStartArray(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypePrefix(List); + } + + @Override + public void writeTypeSuffixForScalar(Object o, JsonGenerator jsonGenerator) throws IOException { + if (canWriteTypeId()) { + writeTypeSuffix(jsonGenerator); + } + } + + @Override + public void writeTypeSuffixForObject(Object o, JsonGenerator jsonGenerator) throws IOException { + jsonGenerator.writeEndObject(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypeSuffix(Map); + } + + @Override + public void writeTypeSuffixForArray(Object o, JsonGenerator jsonGenerator) throws IOException { + jsonGenerator.writeEndArray(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypeSuffix(List); + } + + @Override + public void writeCustomTypePrefixForScalar(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + if (canWriteTypeId()) { + writeTypePrefix(jsonGenerator, s); + } + } + + @Override + public void writeCustomTypePrefixForObject(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + jsonGenerator.writeStartObject(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypePrefix(s); + } + + @Override + public void writeCustomTypePrefixForArray(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + jsonGenerator.writeStartArray(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypePrefix(s); + } + + @Override + public void writeCustomTypeSuffixForScalar(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + if (canWriteTypeId()) { + writeTypeSuffix(jsonGenerator); + } + } + + @Override + public void writeCustomTypeSuffixForObject(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + jsonGenerator.writeEndObject(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypeSuffix(s); + } + + @Override + public void writeCustomTypeSuffixForArray(Object o, JsonGenerator jsonGenerator, String s) throws IOException { + jsonGenerator.writeEndArray(); + // TODO: FULL_TYPES should be implemented here as : if (fullTypesModeEnabled()) writeTypeSuffix(s); + } + + private boolean canWriteTypeId() { + return typeInfo != null + && typeInfo == TypeInfo.PARTIAL_TYPES; + } + + private void writeTypePrefix(JsonGenerator jsonGenerator, String s) throws IOException { + jsonGenerator.writeStartArray(); + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField(this.getPropertyName(), s); + jsonGenerator.writeEndObject(); + } + + private void writeTypeSuffix(JsonGenerator jsonGenerator) throws IOException { + jsonGenerator.writeEndArray(); + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONUtil.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONUtil.java index 9ff427c..3b3ad79 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONUtil.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONUtil.java @@ -55,4 +55,33 @@ public final class GraphSONUtil { serializer.serialize(object, jsonGenerator, serializerProvider); } } + + public static void writeStartObject(Object o, JsonGenerator jsonGenerator, TypeSerializer typeSerializer) throws IOException { + if (typeSerializer != null) + typeSerializer.writeTypePrefixForObject(o, jsonGenerator); + else + jsonGenerator.writeStartObject(); + } + + public static void writeEndObject(Object o, JsonGenerator jsonGenerator, TypeSerializer typeSerializer) throws IOException { + if (typeSerializer != null) + typeSerializer.writeTypeSuffixForObject(o, jsonGenerator); + else + jsonGenerator.writeEndObject(); + } + + public static void writeStartArray(Object o, JsonGenerator jsonGenerator, TypeSerializer typeSerializer) throws IOException { + if (typeSerializer != null) + typeSerializer.writeTypePrefixForArray(o, jsonGenerator); + else + jsonGenerator.writeStartArray(); + } + + + public static void writeEndArray(Object o, JsonGenerator jsonGenerator, TypeSerializer typeSerializer) throws IOException { + if (typeSerializer != null) + typeSerializer.writeTypeSuffixForArray(o, jsonGenerator); + else + jsonGenerator.writeEndArray(); + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java index 37ed9a0..1dd2c9c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java @@ -18,17 +18,11 @@ */ package org.apache.tinkerpop.gremlin.structure.io.graphson; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Element; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.io.GraphWriter; import org.apache.tinkerpop.gremlin.structure.io.Mapper; +import org.apache.tinkerpop.gremlin.structure.util.star.DirectionalStarGraph; import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; -import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializer; import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; import java.io.*; @@ -74,7 +68,7 @@ public final class GraphSONWriter implements GraphWriter { */ @Override public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException { - mapper.writeValue(outputStream, new StarGraphGraphSONSerializer.DirectionalStarGraph(StarGraph.of(v), direction)); + mapper.writeValue(outputStream, new DirectionalStarGraph(StarGraph.of(v), direction)); } /** http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV1d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV1d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV1d0.java index 763c1d9..ba4b056 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV1d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV1d0.java @@ -27,19 +27,7 @@ import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; import java.io.IOException; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.MonthDay; -import java.time.OffsetDateTime; -import java.time.OffsetTime; -import java.time.Period; -import java.time.Year; -import java.time.YearMonth; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; +import java.time.*; /** * GraphSON serializers for classes in {@code java.time.*}.