This is an automated email from the ASF dual-hosted git repository.

kenhuuu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 2722cef70f25ecb60d5d263a36a53473f4f36bf6
Author: Ken Hu <[email protected]>
AuthorDate: Tue Mar 31 00:23:27 2026 -0700

    Update GraphSON4 specification and fix mistakes in Java implementation CTR
---
 docs/src/dev/io/graphson.asciidoc                  | 59 +++++++++++++++++-----
 .../structure/io/graphson/GraphSONMapper.java      |  7 ++-
 .../structure/io/graphson/GraphSONModule.java      |  3 ++
 .../io/graphson/GraphSONSerializersV4.java         | 41 +++++++++++++++
 .../io/graphson/GraphSONTypeSerializerV4.java      | 36 -------------
 .../graphson/GraphSONMapperEmbeddedTypeTest.java   | 14 +++++
 6 files changed, 109 insertions(+), 51 deletions(-)

diff --git a/docs/src/dev/io/graphson.asciidoc 
b/docs/src/dev/io/graphson.asciidoc
index f9cbafff9d..7b3a872d9b 100644
--- a/docs/src/dev/io/graphson.asciidoc
+++ b/docs/src/dev/io/graphson.asciidoc
@@ -27,6 +27,9 @@ Server where arbitrary objects of varying types may be 
returned as results. Howe
 is only intended to be a network serialization format that is only able to 
serialize specific types defined by the
 format. It is only meant to be used with the TinkerPop HTTP API.
 
+NOTE: This document focuses on GraphSON 4. For the GraphSON 1, 2, and 3 
specifications, please refer to the TinkerPop
+3.x IO Documentation.
+
 When considering GraphSON as a "graph" format, the relevant feature to 
consider is the `writeGraph` and `readGraph`
 methods on the `GraphSONWriter` and `GraphSONReader` interfaces, respectively. 
These methods write the entire `Graph`
 instance as output or read an entire `Graph` instance input and they do so in 
a way external to generalized object
@@ -110,6 +113,10 @@ Version 4.0 of GraphSON was first introduced on TinkerPop 
4.0.0 and is represent
 many underused or duplicated types have been removed, labels are now list of 
strings and request/response formats have
 changed quite a bit, and custom types have been replaced with Provider Defined 
Type (PDT).
 
+For each type below, two examples are provided. The first shows the typed 
representation
+(`application/vnd.gremlin-v4.0+json`) and the second shows the untyped 
representation
+(`application/vnd.gremlin-v4.0+json;types=false`).
+
 === Boolean
 
 Matches the JSON Boolean and doesn't have type information.
@@ -424,13 +431,13 @@ JSON String form of UUID.
 
 === Edge
 
-JSON Object (required keys are: id, label, inVLabel, outVLabel, inV, outV) +
+JSON Object (required keys are: id, label, inV, outV) +
 "id" is any GraphSON 4.0 type +
-"inV", "outV" is an Object that contains "id" which is any GraphSON 4.0 type 
and "label" which is a `g:List` of `String`
-"label" is a `g:List` of `String` +
+"inV", "outV" is an Object that contains "id" which is any GraphSON 4.0 type 
and "label" which is a JSON Array of `String`
+"label" is a JSON Array of `String` +
 "properties" is an optional Object containing Arrays of `g:Property`
 
-The untyped version has one additional required key "type" which is always 
"vertex".
+The untyped version has one additional required key "type" which is always 
"edge".
 
 [source,json]
 ----
@@ -2302,8 +2309,8 @@ JSON Object with two required keys: "key" and "value" +
 
 === Tree
 
-JSON Object with one or more possibly nested "key" "value" pairs
-"key" is an Element (`g:Vertex`, `g:Edge`, `g:VertexProperty`)
+JSON Array containing zero or more possibly nested JSON Objects, each 
containing a "key" and "value" pair +
+"key" is an Element (`g:Vertex`, `g:Edge`, `g:VertexProperty`) +
 "value" is a `g:Tree` making this a recursively defined structure
 
 [source,json]
@@ -2431,12 +2438,13 @@ JSON Object with one or more possibly nested "key" 
"value" pairs
 
 === Vertex
 
-JSON Object with required keys: "id", "label", "properties" +
+JSON Object with required keys: "id", "label" +
 "id" is any GraphSON 4.0 type +
-"label" is a `g:List` of `String` +
+"label" is a JSON Array of `String` +
 "properties" is an optional Object containing Arrays of `g:VertexProperty`
 
-The untyped version has one additional required key "type" which is always 
"vertex".
+The untyped version has one additional required key "type" which is always 
"vertex". Also note the difference in how
+labels are written. More details can be found in the `VertexProperty` section.
 
 [source,json]
 ----
@@ -2615,11 +2623,17 @@ The untyped version has one additional required key 
"type" which is always "vert
 
 === VertexProperty
 
-JSON Object with required keys: "id", "value", "label", "properties" +
+JSON Object with required keys: "id", "value", "label" +
 "id" is any type GraphSON 4.0 type +
 "value" is any type GraphSON 4.0 type +
-"label" is a `g:List` of `String` +
-"properties" is an optional Object containing Arrays of "g:Property" 
(metaproperties)
+"label" is a JSON Array of `String` +
+"properties" is an optional Object for metaproperties where each key is a 
meta-property name and each value is any
+GraphSON 4.0 type.
+
+NOTE: When a `VertexProperty` is serialized as a standalone typed value (i.e. 
wrapped in a `g:VertexProperty` envelope),
+the "label" field is always included. However, when vertex properties are 
serialized as part of a `Vertex` in untyped
+mode, the "label" is omitted because the properties are already grouped under 
their property key name in the containing
+object, making the label redundant.
 
 [source,json]
 ----
@@ -2706,6 +2720,23 @@ JSON String of the enum value.
 "label"
 ----
 
+=== Merge
+
+JSON String of the enum value.
+
+[source,json]
+----
+{
+  "@type" : "g:Merge",
+  "@value" : "onCreate"
+}
+----
+
+[source,text]
+----
+"onCreate"
+----
+
 === Standard Request
 
 The following `RequestMessage` is an example of a simple sessionless request 
for a script evaluation with parameters.
@@ -2950,7 +2981,7 @@ The following `ResponseMessage` is a typical example of 
the typical successful r
 
 === Error Result
 
-The following `ResponseMessage` is a typical example of the typical successful 
response Gremlin Server will return when returning results from a script.
+The following `ResponseMessage` is a typical example of an error response the 
Gremlin Server will return from a script.
 
 [source,json]
 ----
@@ -2983,7 +3014,7 @@ The following `ResponseMessage` is a typical example of 
the typical successful r
 }
 ----
 
-Note that the "extended" types require the addition of the separate 
`GraphSONXModuleV4d0` module as follows:
+Note that the "extended" types require the addition of the separate 
`GraphSONXModuleV4` module as follows:
 
 [source,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 68abfac42b..3da5c4e367 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
@@ -106,7 +106,12 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
                     .typeProperty(GraphSONTokens.VALUETYPE);
 
             // Registers native Java types that are supported by Jackson
-            registerJavaBaseTypes(graphSONTypeIdResolver);
+            if (version == GraphSONVersion.V4_0) {
+                graphSONTypeIdResolver.addCustomType(
+                        String.format("%s:%s", 
GraphSONTokens.GREMLIN_TYPE_NAMESPACE, UUID.class.getSimpleName()), UUID.class);
+            } else {
+                registerJavaBaseTypes(graphSONTypeIdResolver);
+            }
 
             // Registers the GraphSON Module's types
             graphSONModule.getTypeDefinitions().forEach(
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 ce87c79bf4..c6d29ad432 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
@@ -201,6 +201,7 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
             // need to explicitly add serializers for these types because 
Jackson doesn't do it at all.
             addSerializer(Integer.class, new 
GraphSONSerializersV4.IntegerGraphSONSerializer());
             addSerializer(Double.class, new 
GraphSONSerializersV4.DoubleGraphSONSerializer());
+            addSerializer(Float.class, new 
GraphSONSerializersV4.FloatGraphSONSerializer());
 
             // traversal
             addSerializer(BulkSet.class, new 
TraversalSerializersV4.BulkSetJacksonSerializer());
@@ -229,10 +230,12 @@ abstract class GraphSONModule extends 
TinkerPopJacksonModule {
             // numbers
             addDeserializer(Integer.class, new 
GraphSONSerializersV4.IntegerJackonsDeserializer());
             addDeserializer(Double.class, new 
GraphSONSerializersV4.DoubleJacksonDeserializer());
+            addDeserializer(Float.class, new 
GraphSONSerializersV4.FloatJacksonDeserializer());
 
             // traversal
             Stream.of(
                     Direction.values(),
+                    Merge.values(),
                     T.values()).flatMap(Stream::of).forEach(e -> 
addDeserializer(e.getClass(), new 
TraversalSerializersV4.EnumJacksonDeserializer(e.getDeclaringClass())));
         }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV4.java
index 92817132cb..66361a0f05 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV4.java
@@ -341,6 +341,18 @@ class GraphSONSerializersV4 {
         }
     }
 
+    final static class FloatGraphSONSerializer extends 
StdScalarSerializer<Float> {
+        public FloatGraphSONSerializer() {
+            super(Float.class);
+        }
+
+        @Override
+        public void serialize(final Float floatValue, final JsonGenerator 
jsonGenerator,
+                              final SerializerProvider serializerProvider) 
throws IOException {
+            jsonGenerator.writeNumber(floatValue);
+        }
+    }
+
     /**
      * Maps in the JVM can have {@link Object} as a key, but in JSON they must 
be a {@link String}.
      */
@@ -627,6 +639,35 @@ class GraphSONSerializersV4 {
         }
     }
 
+    static class FloatJacksonDeserializer extends StdDeserializer<Float> {
+
+        protected FloatJacksonDeserializer() {
+            super(Float.class);
+        }
+
+        @Override
+        public Float deserialize(final JsonParser jsonParser, final 
DeserializationContext deserializationContext) throws IOException, 
JsonProcessingException {
+            if (jsonParser.getCurrentToken().isNumeric())
+                return jsonParser.getFloatValue();
+            else {
+                final String numberText = jsonParser.getValueAsString();
+                if ("NaN".equalsIgnoreCase(numberText))
+                    return Float.NaN;
+                else if ("-Infinity".equals(numberText) || 
"-INF".equalsIgnoreCase(numberText))
+                    return Float.NEGATIVE_INFINITY;
+                else if ("Infinity".equals(numberText) || 
"INF".equals(numberText))
+                    return Float.POSITIVE_INFINITY;
+                else
+                    throw new IllegalStateException("Float value unexpected: " 
+ numberText);
+            }
+        }
+
+        @Override
+        public boolean isCachable() {
+            return true;
+        }
+    }
+
     /**
      * When doing untyped serialization graph objects get a special "type" 
field appended.
      */
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV4.java
index 8f9029c1f3..d581dfe732 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV4.java
@@ -18,33 +18,21 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.graphson;
 
-import org.apache.tinkerpop.gremlin.process.traversal.Operator;
-import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.Path;
-import org.apache.tinkerpop.gremlin.process.traversal.Pick;
-import org.apache.tinkerpop.gremlin.process.traversal.Pop;
-import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
-import org.apache.tinkerpop.gremlin.process.traversal.Scope;
-import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 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.TraversalMetrics;
-import org.apache.tinkerpop.gremlin.structure.Column;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 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.util.function.Lambda;
 import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
 import org.apache.tinkerpop.shaded.jackson.core.JsonToken;
 import org.apache.tinkerpop.shaded.jackson.core.type.WritableTypeId;
 import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver;
 
 import java.io.IOException;
-import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Map;
@@ -137,36 +125,12 @@ public class GraphSONTypeSerializerV4 extends 
AbstractGraphSONTypeSerializer {
             mapped = Path.class;
         else if (VertexProperty.class.isAssignableFrom(c))
             mapped = VertexProperty.class;
-        else if (Metrics.class.isAssignableFrom(c))
-            mapped = Metrics.class;
-        else if (TraversalMetrics.class.isAssignableFrom(c))
-            mapped = TraversalMetrics.class;
         else if (Property.class.isAssignableFrom(c))
             mapped = Property.class;
         else if (ByteBuffer.class.isAssignableFrom(c))
             mapped = ByteBuffer.class;
-        else if (InetAddress.class.isAssignableFrom(c))
-            mapped = InetAddress.class;
-        else if (Lambda.class.isAssignableFrom(c))
-            mapped = Lambda.class;
-        else if (VertexProperty.Cardinality.class.isAssignableFrom(c))
-            mapped = VertexProperty.Cardinality.class;
-        else if (Column.class.isAssignableFrom(c))
-            mapped = Column.class;
         else if (Direction.class.isAssignableFrom(c))
             mapped = Direction.class;
-        else if (Operator.class.isAssignableFrom(c))
-            mapped = Operator.class;
-        else if (Order.class.isAssignableFrom(c))
-            mapped = Order.class;
-        else if (Pop.class.isAssignableFrom(c))
-            mapped = Pop.class;
-        else if (SackFunctions.Barrier.class.isAssignableFrom(c))
-            mapped = SackFunctions.Barrier.class;
-        else if (Pick.class.isAssignableFrom(c))
-            mapped = Pick.class;
-        else if (Scope.class.isAssignableFrom(c))
-            mapped = Scope.class;
         else if (T.class.isAssignableFrom(c))
             mapped = T.class;
         else
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
index b37a70a39f..518e3ae557 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
@@ -149,6 +149,20 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
         assertEquals(o, serializeDeserialize(mapper, o, List.class));
     }
 
+    @Test
+    public void shouldHandleFloatNumberConstants() throws Exception {
+        // Float serializer/deserializer only exists in V4
+        assumeThat(version, startsWith("v4"));
+
+        final List<Object> o = new ArrayList<>();
+        o.add(123.321f);
+        o.add(Float.NaN);
+        o.add(Float.NEGATIVE_INFINITY);
+        o.add(Float.POSITIVE_INFINITY);
+
+        assertEquals(o, serializeDeserialize(mapper, o, List.class));
+    }
+
     @Test
     public void shouldHandleMap() throws Exception {
         assumeThat(version, either(startsWith("v3")).or(startsWith("v4")));

Reply via email to