| /* |
| * 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.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.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.shaded.jackson.databind.ObjectMapper; |
| |
| import java.io.BufferedWriter; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.util.Iterator; |
| import java.util.function.Function; |
| |
| /** |
| * A @{link GraphWriter} that writes a graph and its elements to a JSON-based representation. This implementation |
| * only supports JSON data types and is therefore lossy with respect to data types (e.g. a float will become a double). |
| * Further note that serialized {@code Map} objects do not support complex types for keys. {@link Edge} and |
| * {@link Vertex} objects are serialized to {@code Map} instances. If an |
| * {@link Element} is used as a key, it is coerced to its identifier. Other complex |
| * objects are converted via {@link Object#toString()} unless a mapper serializer is supplied. |
| * |
| * @author Stephen Mallette (http://stephen.genoprime.com) |
| */ |
| public final class GraphSONWriter implements GraphWriter { |
| private final ObjectMapper mapper; |
| private final boolean wrapAdjacencyList; |
| |
| private GraphSONWriter(final Builder builder) { |
| mapper = builder.mapper.createMapper(); |
| wrapAdjacencyList = builder.wrapAdjacencyList; |
| } |
| |
| /** |
| * Writes a {@link Graph} to stream in an adjacency list format where vertices are written with edges from both |
| * directions. Under this serialization model, edges are grouped by label. |
| * |
| * @param outputStream the stream to write to. |
| * @param g the graph to write to stream. |
| */ |
| @Override |
| public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException { |
| writeVertices(outputStream, g.vertices(), Direction.BOTH); |
| } |
| |
| /** |
| * Writes a single {@link Vertex} to stream where edges only from the specified direction are written. |
| * Under this serialization model, edges are grouped by label. |
| * |
| * @param direction the direction of edges to write or null if no edges are to be written. |
| */ |
| @Override |
| public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException { |
| mapper.writeValue(outputStream, new DirectionalStarGraph(StarGraph.of(v), direction)); |
| } |
| |
| /** |
| * Writes a single {@link Vertex} with no edges serialized. |
| * |
| * @param outputStream the stream to write to. |
| * @param v the vertex to write. |
| */ |
| @Override |
| public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException { |
| writeVertex(outputStream, v, null); |
| } |
| |
| /** |
| * Writes a list of vertices in adjacency list format where vertices are written with edges from both |
| * directions. Under this serialization model, edges are grouped by label. |
| * |
| * @param outputStream the stream to write to. |
| * @param vertexIterator a traversal that returns a list of vertices. |
| * @param direction if direction is null then no edges are written. |
| */ |
| @Override |
| public void writeVertices(final OutputStream outputStream, final Iterator<Vertex> vertexIterator, final Direction direction) throws IOException { |
| final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream)); |
| try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { |
| if (wrapAdjacencyList) writer.write("{\"" + GraphSONTokens.VERTICES + "\":["); |
| while (vertexIterator.hasNext()) { |
| writeVertex(baos, vertexIterator.next(), direction); |
| writer.write(new String(baos.toByteArray())); |
| if (wrapAdjacencyList) { |
| if (vertexIterator.hasNext()) |
| writer.write(","); |
| } else { |
| writer.newLine(); |
| } |
| baos.reset(); |
| } |
| if (wrapAdjacencyList) writer.write("]}"); |
| } |
| |
| writer.flush(); |
| } |
| |
| /** |
| * Writes a list of vertices without edges. |
| * |
| * @param outputStream the stream to write to. |
| * @param vertexIterator a iterator that returns a list of vertices. |
| */ |
| @Override |
| public void writeVertices(final OutputStream outputStream, final Iterator<Vertex> vertexIterator) throws IOException { |
| writeVertices(outputStream, vertexIterator, null); |
| } |
| |
| /** |
| * Writes an {@link Edge} object to the stream. Note that this format is different from the format of an |
| * {@link Edge} when serialized with a {@link Vertex} as done with |
| * {@link #writeVertex(OutputStream, Vertex, Direction)} or |
| * {@link #writeVertices(OutputStream, Iterator, Direction)} in that the edge label is part of the object and |
| * vertex labels are included with their identifiers. |
| * |
| * @param outputStream the stream to write to. |
| * @param e the edge to write. |
| */ |
| @Override |
| public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException { |
| mapper.writeValue(outputStream, e); |
| } |
| |
| /** |
| * Write a {@link VertexProperty} object to the stream. |
| * |
| * @param outputStream the stream to write to. |
| * @param vp the vertex property to write. |
| */ |
| @Override |
| public void writeVertexProperty(final OutputStream outputStream, final VertexProperty vp) throws IOException { |
| mapper.writeValue(outputStream, vp); |
| } |
| |
| /** |
| * Write a {@link Property} object to the stream. |
| * |
| * @param outputStream the stream to write to. |
| * @param p the property to write. |
| */ |
| @Override |
| public void writeProperty(final OutputStream outputStream, final Property p) throws IOException { |
| mapper.writeValue(outputStream, p); |
| } |
| |
| /** |
| * Writes an arbitrary object to the stream. Note that Gremlin Server uses this method when serializing output, |
| * thus the format of the GraphSON for a {@link Vertex} will be somewhat different from the format supplied |
| * when using {@link #writeVertex(OutputStream, Vertex, Direction)}. For example, edges will never be included. |
| * |
| * @param outputStream the stream to write to |
| * @param object the object to write which will use the standard serializer set |
| */ |
| @Override |
| public void writeObject(final OutputStream outputStream, final Object object) throws IOException { |
| this.mapper.writeValue(outputStream, object); |
| } |
| |
| public static Builder build() { |
| return new Builder(); |
| } |
| |
| public static class Builder implements WriterBuilder<GraphSONWriter> { |
| |
| private Mapper<ObjectMapper> mapper = GraphSONMapper.build().create(); |
| private boolean wrapAdjacencyList = false; |
| |
| private Builder() { } |
| |
| /** |
| * Override all of the builder options with this mapper. If this value is set to something other than |
| * null then that value will be used to construct the writer. |
| */ |
| public Builder mapper(final Mapper<ObjectMapper> mapper) { |
| this.mapper = mapper; |
| return this; |
| } |
| |
| /** |
| * Wraps the output of {@link #writeGraph(OutputStream, Graph)}, {@link #writeVertices(OutputStream, Iterator)} |
| * and {@link #writeVertices(OutputStream, Iterator, Direction)} in a JSON object. By default, this value |
| * is {@code false} which means that the output is such that there is one JSON object (vertex) per line. |
| * When {@code true} the line breaks are not written and instead a valid JSON object is formed where the |
| * vertices are part of a JSON array in a key called "vertices". |
| * <p/> |
| * By setting this value to {@code true}, the generated JSON is no longer "splittable" by line and thus not |
| * suitable for OLAP processing. Furthermore, reading this format of the JSON with |
| * {@link GraphSONReader#readGraph(InputStream, Graph)} or |
| * {@link GraphSONReader#readVertices(InputStream, Function, Function, Direction)} requires that the |
| * entire JSON object be read into memory, so it is best saved for "small" graphs. |
| */ |
| public Builder wrapAdjacencyList(final boolean wrapAdjacencyListInObject) { |
| this.wrapAdjacencyList = wrapAdjacencyListInObject; |
| return this; |
| } |
| |
| public GraphSONWriter create() { |
| return new GraphSONWriter(this); |
| } |
| } |
| } |