| /* |
| * 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.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.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.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.concurrent.TimeUnit; |
| |
| /** |
| * 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) |
| */ |
| final class GraphSONSerializersV1d0 { |
| |
| private GraphSONSerializersV1d0() {} |
| |
| 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| serializerProvider.defaultSerializeField(GraphSONTokens.KEY, property.key(), jsonGenerator); |
| serializerProvider.defaultSerializeField(GraphSONTokens.VALUE, property.value(), jsonGenerator); |
| jsonGenerator.writeEndObject(); |
| } |
| } |
| |
| 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| 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); |
| jsonGenerator.writeEndObject(); |
| } |
| |
| 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.writeObjectFieldStart(GraphSONTokens.PROPERTIES); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| while (elementProperties.hasNext()) { |
| final Property<Object> elementProperty = elementProperties.next(); |
| GraphSONUtil.writeWithType(elementProperty.key(), elementProperty.value(), jsonGenerator, serializerProvider, typeSerializer); |
| } |
| jsonGenerator.writeEndObject(); |
| } |
| } |
| |
| } |
| |
| 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| 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); |
| jsonGenerator.writeEndObject(); |
| } |
| |
| private void writeProperties(final Vertex vertex, final JsonGenerator jsonGenerator, |
| final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { |
| jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| |
| 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.writeArrayFieldStart(key); |
| if (typeSerializer != null) { |
| jsonGenerator.writeString(ArrayList.class.getName()); |
| jsonGenerator.writeStartArray(); |
| } |
| |
| while (vertexProperties.hasNext()) { |
| serializerVertexProperty(vertexProperties.next(), jsonGenerator, serializerProvider, typeSerializer, normalize, false); |
| } |
| |
| jsonGenerator.writeEndArray(); |
| if (typeSerializer != null) jsonGenerator.writeEndArray(); |
| } |
| } |
| |
| jsonGenerator.writeEndObject(); |
| } |
| |
| } |
| |
| 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| jsonGenerator.writeObjectField(GraphSONTokens.LABELS, path.labels()); |
| jsonGenerator.writeObjectField(GraphSONTokens.OBJECTS, path.objects()); |
| jsonGenerator.writeEndObject(); |
| } |
| |
| } |
| |
| 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| |
| Set<Map.Entry<Element, Tree>> set = tree.entrySet(); |
| for(Map.Entry<Element, Tree> entry : set) |
| { |
| jsonGenerator.writeObjectFieldStart(entry.getKey().id().toString()); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| jsonGenerator.writeObjectField(GraphSONTokens.KEY, entry.getKey()); |
| jsonGenerator.writeObjectField(GraphSONTokens.VALUE, entry.getValue()); |
| jsonGenerator.writeEndObject(); |
| } |
| jsonGenerator.writeEndObject(); |
| } |
| } |
| |
| /** |
| * 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 { |
| jsonGenerator.writeStartObject(); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| 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); |
| jsonGenerator.writeEndObject(); |
| } |
| |
| 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.writeObjectFieldStart(GraphSONTokens.PROPERTIES); |
| if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); |
| 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); |
| } |
| jsonGenerator.writeEndObject(); |
| } |
| |
| } |