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

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

commit 7ddb67a4a08de5b4cfe41f06b2749c1f23a64133
Author: Ken Hu <[email protected]>
AuthorDate: Fri Jul 17 21:14:39 2026 -0700

    Update GraphBinary/GraphSON model round-trip coverage.
    
    Assisted-by: Codex:gpt-5.5
---
 docs/src/reference/gremlin-variants.asciidoc       |  22 +-
 .../io/graphson/PdtGraphSONSerializersV4.java      |  14 +-
 .../io/graphson/PdtGraphSONSerializersV4Test.java  |  27 +-
 .../Structure/IO/GraphBinary4/Model.cs             | 162 ++++++++++-
 .../Structure/IO/GraphBinary4/RoundTripTests.cs    | 262 +++++++++++++++++-
 .../structure/io/binary/internals/MapSerializer.js |   2 +-
 .../test/unit/graphbinary/model-test.js            | 157 ++++++++---
 .../test/unit/graphbinary/model.js                 | 140 +++++++++-
 .../gremlin_python/structure/io/graphbinaryV4.py   |   2 +-
 .../main/python/tests/unit/structure/io/model.py   | 134 ++++++++-
 .../unit/structure/io/test_graphbinaryv4model.py   | 188 +++++++++++--
 .../io/graphbinary/empty-label-vertex-v4.gbin      | Bin 0 -> 18 bytes
 .../structure/io/graphbinary/empty-string-v4.gbin  | Bin 0 -> 6 bytes
 .../structure/io/graphbinary/empty-tree-v4.gbin    | Bin 0 -> 6 bytes
 .../io/graphbinary/four-byte-char-v4.gbin          | Bin 0 -> 6 bytes
 .../structure/io/graphbinary/merge-in-v-v4.gbin    | Bin 0 -> 11 bytes
 .../io/graphbinary/merge-on-create-v4.gbin         | Bin 0 -> 16 bytes
 .../io/graphbinary/merge-on-match-v4.gbin          | Bin 0 -> 15 bytes
 .../structure/io/graphbinary/merge-out-v-v4.gbin   | Bin 0 -> 12 bytes
 .../io/graphbinary/multi-label-vertex-v4.gbin      | Bin 0 -> 44 bytes
 .../io/graphbinary/nanos-duration-v4.gbin          | Bin 0 -> 14 bytes
 .../io/graphbinary/negative-duration-v4.gbin       | Bin 0 -> 14 bytes
 .../graphbinary/negative-scale-bigdecimal-v4.gbin  | Bin 0 -> 12 bytes
 .../structure/io/graphbinary/null-int-v4.gbin      |   1 +
 .../structure/io/graphbinary/null-list-v4.gbin     |   1 +
 .../structure/io/graphbinary/null-long-v4.gbin     |   1 +
 .../structure/io/graphbinary/null-map-v4.gbin      |   2 +
 .../structure/io/graphbinary/null-set-v4.gbin      |   1 +
 .../structure/io/graphbinary/null-string-v4.gbin   |   1 +
 .../io/graphbinary/ordered-string-int-map-v4.gbin  | Bin 0 -> 111 bytes
 .../io/graphbinary/path-multiple-labels-v4.gbin    | Bin 0 -> 45 bytes
 .../io/graphbinary/path-zero-labels-v4.gbin        | Bin 0 -> 31 bytes
 .../io/graphbinary/point-composite-pdt-v4.gbin     | Bin 0 -> 45 bytes
 .../io/graphbinary/positive-duration-v4.gbin       | Bin 0 -> 14 bytes
 .../io/graphbinary/scale-zero-bigdecimal-v4.gbin   | Bin 0 -> 12 bytes
 .../sign-boundary-neg-biginteger-v4.gbin           | Bin 0 -> 8 bytes
 .../sign-boundary-pos-biginteger-v4.gbin           | Bin 0 -> 8 bytes
 .../graphbinary/small-decimal-bigdecimal-v4.gbin   | Bin 0 -> 12 bytes
 .../io/graphbinary/three-byte-char-v4.gbin         | Bin 0 -> 5 bytes
 .../io/graphbinary/tree-deep-nesting-v4.gbin       | Bin 0 -> 50 bytes
 .../io/graphbinary/tree-mixed-key-types-v4.gbin    | Bin 0 -> 30 bytes
 .../structure/io/graphbinary/tree-null-key-v4.gbin | Bin 0 -> 12 bytes
 ...lti-byte-char-v4.gbin => two-byte-char-v4.gbin} | Bin
 .../io/graphbinary/uint8-primitive-pdt-v4.gbin     | Bin 0 -> 21 bytes
 .../io/graphbinary/zero-bigdecimal-v4.gbin         | Bin 0 -> 11 bytes
 .../io/graphbinary/zero-biginteger-v4.gbin         | Bin 0 -> 7 bytes
 .../io/graphson/empty-label-vertex-v4.json         |  10 +
 .../structure/io/graphson/empty-string-v4.json     |   1 +
 .../structure/io/graphson/merge-in-v-v4.json       |   4 +
 .../structure/io/graphson/merge-on-create-v4.json  |   4 +
 .../structure/io/graphson/merge-on-match-v4.json   |   4 +
 .../structure/io/graphson/merge-out-v-v4.json      |   4 +
 .../io/graphson/multi-label-vertex-v4.json         |  10 +
 .../structure/io/graphson/nanos-duration-v4.json   |   4 +
 .../io/graphson/negative-duration-v4.json          |   4 +
 .../io/graphson/negative-scale-bigdecimal-v4.json  |   4 +
 .../io/graphson/ordered-string-int-map-v4.json     |  22 ++
 .../io/graphson/path-multiple-labels-v4.json       |  16 ++
 .../structure/io/graphson/path-zero-labels-v4.json |  16 ++
 .../io/graphson/point-composite-pdt-v4.json        |  16 ++
 .../io/graphson/positive-duration-v4.json          |   4 +
 .../io/graphson/scale-zero-bigdecimal-v4.json      |   4 +
 .../graphson/sign-boundary-neg-biginteger-v4.json  |   4 +
 .../graphson/sign-boundary-pos-biginteger-v4.json  |   4 +
 .../io/graphson/small-decimal-bigdecimal-v4.json   |   4 +
 ...i-byte-char-v4.json => three-byte-char-v4.json} |   2 +-
 ...lti-byte-char-v4.json => two-byte-char-v4.json} |   0
 .../io/graphson/uint8-primitive-pdt-v4.json        |   7 +
 .../structure/io/graphson/zero-bigdecimal-v4.json  |   4 +
 .../structure/io/graphson/zero-biginteger-v4.json  |   4 +
 .../io/AbstractTypedCompatibilityTest.java         | 303 ++++++++++++++++++++-
 .../tinkerpop/gremlin/structure/io/Model.java      |  76 +++++-
 .../graphbinary/GraphBinaryCompatibilityTest.java  | 104 +++++++
 73 files changed, 1643 insertions(+), 113 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc 
b/docs/src/reference/gremlin-variants.asciidoc
index 3bc95a6fb7..0df31daa3d 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -737,6 +737,11 @@ that can be used to fulfill the `gremlingo.Set` interface 
if desired.
 * Go does not support ordered maps natively as the built-in `map` type does 
not guarantee iteration order. Traversal
 results which contain maps may not preserve original ordering when 
deserialized into Go's native map types.
 
+* GraphBinary `Char` values are not supported.
+
+* GraphBinary permits map keys of any supported type, including composite 
values such as lists. Go's native `map` type
+only supports comparable keys, so GraphBinary maps with non-comparable keys 
are not supported.
+
 * The `subgraph()`-step returns a detached `*Graph` data container exposing
 `Vertices map[interface{}]*Vertex` and `Edges map[interface{}]*Edge`. The 
result is not a live `Graph` instance:
 mutating the maps has no effect on the source graph, and it cannot be passed 
to `traversal().with(...)`. To
@@ -1260,6 +1265,12 @@ The `IoRegistry` tells the serializer what classes from 
the graph provider to au
 Gremlin Server roughly uses this same approach when it configures its 
serializers, so using this same model will
 ensure compatibility when making requests.
 
+[[gremlin-java-limitations]]
+=== Limitations
+
+* Java `Character` values do not support values outside the Basic Multilingual 
Plane, which are mainly 4-byte UTF-8
+characters. Those GraphBinary `Char` values are not supported.
+
 [[gremlin-java-gvalue]]
 === GValue Parameterization
 
@@ -2500,12 +2511,15 @@ unwrap('hello');    // 'hello'
 
 * JavaScript's `Number` type is an IEEE 754 double-precision float. `Float`, 
`Byte`, and `Short` values from the server
 are deserialized as `Number` and lose their original type information. Use 
`preciseNumbers: true` to preserve the
-original types — see <<gremlin-javascript-numeric-types>>.
+original types. See <<gremlin-javascript-numeric-types>>.
 * `Long` values outside the safe integer range (|n| > 2^53 - 1) are 
deserialized as `BigInt` to preserve precision.
 Values within the safe range are deserialized as `Number`. The same server 
type may produce different JavaScript types.
 * `Number.isInteger(1.0)` is `true` in JavaScript, so the driver cannot 
distinguish integer values from whole-number
-doubles. `BigDecimal` is not implemented. Typed wrappers (e.g. `toInt()`, 
`toDouble()`) can be used to control the
-exact type sent to the server — see <<gremlin-javascript-numeric-types>>.
+doubles. Typed wrappers (e.g. `toInt()`, `toDouble()`) can be used to control 
the exact type sent to the server. See
+<<gremlin-javascript-numeric-types>>.
+* GraphBinary `BigDecimal`, `Char`, and `Duration` values are not implemented.
+* JavaScript `Date` values do not support the extreme values of GraphBinary's 
`DateTime` maximum and minimum. Those
+GraphBinary `DateTime` values are not supported.
 * The driver applies GremlinLang type suffixes automatically based on value 
characteristics: integers within the 32-bit
 signed range are unsuffixed (Int), integers beyond that up to 
`Number.MAX_SAFE_INTEGER` use the `L` suffix (Long),
 non-integer numbers and integers beyond the safe range use the `D` suffix 
(Double), and `BigInt` values use the `N`
@@ -3100,6 +3114,8 @@ so offset date-time values at those boundaries will fail 
to deserialize.
 `Duration.FOREVER`) that exceed `TimeSpan.MaxValue` or `TimeSpan.MinValue` 
will fail to deserialize.
 * Gremlin's `BigDecimal` supports up to 33 digits of precision while C#'s 
`decimal` type is limited to 28-29
 significant digits, so high-precision values may lose accuracy or fail to 
deserialize.
+* C# `char` values do not support values outside the Basic Multilingual Plane, 
which are mainly 4-byte UTF-8
+characters. Those GraphBinary `Char` values are not supported.
 * C# `Dictionary` does not allow `null` keys, so `Map` results with `null` 
keys (e.g. from `group()` or
 `groupCount()` on a missing property) will fail during deserialization.
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
index a4bcd391ad..f9652b2591 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
@@ -57,12 +57,7 @@ final class PdtGraphSONSerializersV4 {
             jsonGenerator.writeStartObject();
             jsonGenerator.writeStringField("type", pdt.getName());
             jsonGenerator.writeFieldName("fields");
-            jsonGenerator.writeStartObject();
-            for (final Map.Entry<String, Object> entry : 
pdt.getFields().entrySet()) {
-                jsonGenerator.writeFieldName(entry.getKey());
-                jsonGenerator.writeObject(entry.getValue());
-            }
-            jsonGenerator.writeEndObject();
+            jsonGenerator.writeObject(pdt.getFields());
             jsonGenerator.writeEndObject();
         }
     }
@@ -92,12 +87,7 @@ final class PdtGraphSONSerializersV4 {
                     typeName = jsonParser.getText();
                 } else if ("fields".equals(fieldName)) {
                     jsonParser.nextToken();
-                    while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
-                        final String key = jsonParser.getCurrentName();
-                        jsonParser.nextToken();
-                        final Object value = 
deserializationContext.readValue(jsonParser, Object.class);
-                        fields.put(key, value);
-                    }
+                    fields = deserializationContext.readValue(jsonParser, 
Map.class);
                 }
             }
 
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
index 6da520d0e0..f153e95ce4 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
@@ -70,15 +70,18 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         assertEquals("Point", value.get("type").asText());
 
         final JsonNode fields = value.get("fields");
-        assertEquals("g:Int32", fields.get("x").get("@type").asText());
-        assertEquals(1, fields.get("x").get("@value").asInt());
-        assertEquals("g:Int32", fields.get("y").get("@type").asText());
-        assertEquals(2, fields.get("y").get("@value").asInt());
+        assertEquals("g:Map", fields.get("@type").asText());
+        final JsonNode x = getMapValue(fields, "x");
+        assertEquals("g:Int32", x.get("@type").asText());
+        assertEquals(1, x.get("@value").asInt());
+        final JsonNode y = getMapValue(fields, "y");
+        assertEquals("g:Int32", y.get("@type").asText());
+        assertEquals(2, y.get("@value").asInt());
     }
 
     @Test
     public void shouldDeserializeValidJson() throws Exception {
-        final String json = 
"{\"@type\":\"g:CompositePdt\",\"@value\":{\"type\":\"Point\",\"fields\":{\"x\":{\"@type\":\"g:Int32\",\"@value\":1},\"y\":{\"@type\":\"g:Int32\",\"@value\":2}}}}";
+        final String json = 
"{\"@type\":\"g:CompositePdt\",\"@value\":{\"type\":\"Point\",\"fields\":{\"@type\":\"g:Map\",\"@value\":[\"x\",{\"@type\":\"g:Int32\",\"@value\":1},\"y\",{\"@type\":\"g:Int32\",\"@value\":2}]}}}";
         final CompositePDT pdt = mapper.readValue(json, CompositePDT.class);
 
         assertEquals("Point", pdt.getName());
@@ -117,7 +120,7 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
 
         assertEquals("g:CompositePdt", node.get("@type").asText());
         final JsonNode fields = node.get("@value").get("fields");
-        final JsonNode locationNode = fields.get("location");
+        final JsonNode locationNode = getMapValue(fields, "location");
         assertEquals("g:CompositePdt", locationNode.get("@type").asText());
         assertEquals("Point", locationNode.get("@value").get("type").asText());
 
@@ -318,7 +321,7 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         final JsonNode node = plainMapper.readTree(json);
 
         assertEquals("g:CompositePdt", node.get("@type").asText());
-        final JsonNode durNode = node.get("@value").get("fields").get("dur");
+        final JsonNode durNode = getMapValue(node.get("@value").get("fields"), 
"dur");
         assertEquals("g:PrimitivePdt", durNode.get("@type").asText());
         assertEquals("Duration", durNode.get("@value").get("type").asText());
         assertEquals("PT1H", durNode.get("@value").get("value").asText());
@@ -332,6 +335,16 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         assertEquals("PT1H", nestedResult.getValue());
     }
 
+    private JsonNode getMapValue(final JsonNode graphsonMap, final String key) 
{
+        final JsonNode values = graphsonMap.get("@value");
+        for (int ix = 0; ix < values.size(); ix = ix + 2) {
+            if (key.equals(values.get(ix).asText()))
+                return values.get(ix + 1);
+        }
+
+        throw new IllegalArgumentException("Could not find key in GraphSON 
map: " + key);
+    }
+
     // helper types for primitive PDT hydration tests
 
     static class MyDuration {
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
index bc92428e9d..b161044af2 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
@@ -23,6 +23,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Numerics;
 using Gremlin.Net.Process.Traversal;
 using Gremlin.Net.Structure;
@@ -33,12 +34,12 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
     /// Defines the supported types for GraphBinary 4.0 IO and provides test 
entries for round-trip testing.
     /// 
     /// The following models aren't supported:
-    /// tinker-graph              Graph type not implemented
     /// max-offsetdatetime        DateTimeOffset.MaxValue exceeds 
serialization range
     /// min-offsetdatetime        DateTimeOffset.MinValue exceeds 
serialization range
     /// forever-duration          TimeSpan cannot represent Duration.FOREVER
     /// pos-bigdecimal            Java BigDecimal precision (33 digits) 
exceeds C# decimal (28-29 digits)
     /// neg-bigdecimal            Java BigDecimal precision (33 digits) 
exceeds C# decimal (28-29 digits)
+    /// four-byte-char            C# char cannot represent a supplementary 
Unicode code point as a single scalar value
     /// var-type-map              Dictionary doesn't support null key
     /// </summary>
     public static class Model
@@ -70,11 +71,50 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             return tree;
         }
 
+        private static Tree BuildNullKeyTree()
+        {
+            var tree = new Tree();
+            tree.GetOrCreateChild(null);
+            return tree;
+        }
+
+        private static Tree BuildMixedKeyTypesTree()
+        {
+            var tree = new Tree();
+            tree.GetOrCreateChild("name");
+            tree.GetOrCreateChild(123);
+            return tree;
+        }
+
+        private static Tree BuildDeepNestingTree()
+        {
+            var tree = new Tree();
+            
tree.GetOrCreateChild("root").GetOrCreateChild("branch").GetOrCreateChild("leaf");
+            return tree;
+        }
+
         public static Dictionary<string, object?> Entries { get; } = new()
         {
+            // BigDecimal
+            ["zero-bigdecimal"] = 0m,
+            ["scale-zero-bigdecimal"] = 1234m,
+            ["negative-scale-bigdecimal"] = 123400m,
+            ["small-decimal-bigdecimal"] = 12.34m,
+
             // BigInteger
             ["pos-biginteger"] = 
BigInteger.Parse("123456789987654321123456789987654321"),
             ["neg-biginteger"] = 
BigInteger.Parse("-123456789987654321123456789987654321"),
+            ["zero-biginteger"] = BigInteger.Zero,
+            ["sign-boundary-pos-biginteger"] = new BigInteger(128),
+            ["sign-boundary-neg-biginteger"] = new BigInteger(-129),
+
+            // Provider-defined types
+            ["uint8-primitive-pdt"] = new PrimitivePDT("Uint8", "10"),
+            ["point-composite-pdt"] = new CompositePDT("Point", new 
Dictionary<string, object?>
+            {
+                { "x", 1 },
+                { "y", 2 },
+            }),
             
             // Byte (sbyte in C#)
             ["min-byte"] = sbyte.MinValue,  // -128
@@ -108,10 +148,17 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Char
             ["single-byte-char"] = 'a',
-            ["multi-byte-char"] = '\u03A9',  // Greek capital letter Omega
+            ["two-byte-char"] = '\u03A9',  // Greek capital letter Omega
+            ["three-byte-char"] = '\u20AC',
             
             // Null
             ["unspecified-null"] = null,
+            ["null-int"] = null,
+            ["null-long"] = null,
+            ["null-string"] = null,
+            ["null-list"] = null,
+            ["null-map"] = null,
+            ["null-set"] = null,
             
             // Boolean
             ["true-boolean"] = true,
@@ -120,6 +167,7 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             // String
             ["single-byte-string"] = "abc",
             ["mixed-string"] = "abc\u0391\u0392\u0393",  // abc + Greek 
letters Alpha, Beta, Gamma
+            ["empty-string"] = "",
             
             // BulkSet (represented as List with duplicates)
             ["var-bulklist"] = new List<object?> { "marko", "josh", "josh" },
@@ -127,11 +175,15 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Duration (TimeSpan)
             ["zero-duration"] = TimeSpan.Zero,
+            ["positive-duration"] = TimeSpan.FromSeconds(123),
+            ["negative-duration"] = TimeSpan.FromSeconds(-123),
+            ["nanos-duration"] = TimeSpan.FromSeconds(123) + 
TimeSpan.FromTicks(4567),
             
             // Edge
             ["traversal-edge"] = new Edge(13, new Vertex(1, "person"), 
"develops", new Vertex(10, "software"),
                 new dynamic[] { SinceProperty }),
             ["no-prop-edge"] = new Edge(13, new Vertex(1, "person"), 
"develops", new Vertex(10, "software")),
+            ["tinker-graph"] = CrewGraphFactory.Create(),
             
             // Int
             ["max-int"] = int.MaxValue,
@@ -154,16 +206,34 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
                 // Note: null key not supported in C# Dictionary
             },
             ["empty-map"] = new Dictionary<object, object?>(),
+            ["ordered-string-int-map"] = new Dictionary<object, object?>
+            {
+                { "delta", 4 },
+                { "alpha", 1 },
+                { "charlie", 3 },
+                { "bravo", 2 },
+                { "echo", 5 },
+                { "foxtrot", 6 },
+            },
             
             // Path
             ["traversal-path"] = new Path(
                 new List<ISet<string>> { new HashSet<string>(), new 
HashSet<string>(), new HashSet<string>() },
                 new List<object?> { new Vertex(1, "person"), new Vertex(10, 
"software"), new Vertex(11, "software") }),
             ["empty-path"] = new Path(new List<ISet<string>>(), new 
List<object?>()),
+            ["path-zero-labels"] = new Path(
+                new List<ISet<string>> { new HashSet<string>() },
+                new List<object?> { "marko" }),
+            ["path-multiple-labels"] = new Path(
+                new List<ISet<string>> { new HashSet<string> { "a", "b" } },
+                new List<object?> { "marko" }),
 
             // Tree
             ["traversal-tree"] = BuildTraversalTree(),
             ["empty-tree"] = new Tree(),
+            ["tree-null-key"] = BuildNullKeyTree(),
+            ["tree-mixed-key-types"] = BuildMixedKeyTypesTree(),
+            ["tree-deep-nesting"] = BuildDeepNestingTree(),
 
             ["prop-path"] = new Path(
                 new List<ISet<string>> { new HashSet<string>(), new 
HashSet<string>(), new HashSet<string>() },
@@ -199,6 +269,8 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Vertex
             ["no-prop-vertex"] = new Vertex(1, "person"),
+            ["multi-label-vertex"] = new Vertex(1, "person", labels: new[] { 
"person", "employee" }),
+            ["empty-label-vertex"] = new Vertex(1, "", labels: 
Array.Empty<string>()),
             ["traversal-vertex"] = new Vertex(1, "person", new dynamic[]
             {
                 new Property("name", NameMarko, null),
@@ -217,6 +289,92 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Direction enum
             ["out-direction"] = Direction.Out,
+
+            // Merge enum
+            ["merge-on-create"] = Merge.OnCreate,
+            ["merge-on-match"] = Merge.OnMatch,
+            ["merge-out-v"] = Merge.OutV,
+            ["merge-in-v"] = Merge.InV,
         };
+
+        private static class CrewGraphFactory
+        {
+            public static Graph Create()
+            {
+                var graph = new Graph();
+                var v1 = AddVertex(graph, 1, "person",
+                    Vp(0L, "name", "marko"),
+                    Vp(6L, "location", "san diego", new Property("startTime", 
1997, null),
+                        new Property("endTime", 2001, null)),
+                    Vp(7L, "location", "santa cruz", new Property("startTime", 
2001, null),
+                        new Property("endTime", 2004, null)),
+                    Vp(8L, "location", "brussels", new Property("startTime", 
2004, null),
+                        new Property("endTime", 2005, null)),
+                    Vp(9L, "location", "santa fe", new Property("startTime", 
2005, null)));
+                var v7 = AddVertex(graph, 7, "person",
+                    Vp(1L, "name", "stephen"),
+                    Vp(10L, "location", "centreville", new 
Property("startTime", 1990, null),
+                        new Property("endTime", 2000, null)),
+                    Vp(11L, "location", "dulles", new Property("startTime", 
2000, null),
+                        new Property("endTime", 2006, null)),
+                    Vp(12L, "location", "purcellville", new 
Property("startTime", 2006, null)));
+                var v8 = AddVertex(graph, 8, "person",
+                    Vp(2L, "name", "matthias"),
+                    Vp(13L, "location", "bremen", new Property("startTime", 
2004, null),
+                        new Property("endTime", 2007, null)),
+                    Vp(14L, "location", "baltimore", new Property("startTime", 
2007, null),
+                        new Property("endTime", 2011, null)),
+                    Vp(15L, "location", "oakland", new Property("startTime", 
2011, null),
+                        new Property("endTime", 2014, null)),
+                    Vp(16L, "location", "seattle", new Property("startTime", 
2014, null)));
+                var v9 = AddVertex(graph, 9, "person",
+                    Vp(3L, "name", "daniel"),
+                    Vp(17L, "location", "spremberg", new Property("startTime", 
1982, null),
+                        new Property("endTime", 2005, null)),
+                    Vp(18L, "location", "kaiserslautern", new 
Property("startTime", 2005, null),
+                        new Property("endTime", 2009, null)),
+                    Vp(19L, "location", "aachen", new Property("startTime", 
2009, null)));
+                var v10 = AddVertex(graph, 10, "software", Vp(4L, "name", 
"gremlin"));
+                var v11 = AddVertex(graph, 11, "software", Vp(5L, "name", 
"tinkergraph"));
+
+                AddEdge(graph, 13, v1, "develops", v10, new Property("since", 
2009, null));
+                AddEdge(graph, 14, v1, "develops", v11, new Property("since", 
2010, null));
+                AddEdge(graph, 15, v1, "uses", v10, new Property("skill", 4, 
null));
+                AddEdge(graph, 16, v1, "uses", v11, new Property("skill", 5, 
null));
+                AddEdge(graph, 17, v7, "develops", v10, new Property("since", 
2010, null));
+                AddEdge(graph, 18, v7, "develops", v11, new Property("since", 
2011, null));
+                AddEdge(graph, 19, v7, "uses", v10, new Property("skill", 5, 
null));
+                AddEdge(graph, 20, v7, "uses", v11, new Property("skill", 4, 
null));
+                AddEdge(graph, 21, v8, "develops", v10, new Property("since", 
2012, null));
+                AddEdge(graph, 22, v8, "uses", v10, new Property("skill", 3, 
null));
+                AddEdge(graph, 23, v8, "uses", v11, new Property("skill", 3, 
null));
+                AddEdge(graph, 24, v9, "uses", v10, new Property("skill", 5, 
null));
+                AddEdge(graph, 25, v9, "uses", v11, new Property("skill", 3, 
null));
+                AddEdge(graph, 26, v10, "traverses", v11);
+
+                return graph;
+            }
+
+            private static VertexProperty Vp(object id, string label, object? 
value, params Property[] metaProperties)
+            {
+                return new VertexProperty(id, label, value, null, 
metaProperties.Cast<dynamic>().ToArray());
+            }
+
+            private static Vertex AddVertex(Graph graph, object id, string 
label,
+                params VertexProperty[] vertexProperties)
+            {
+                var vertex = new Vertex(id, label, 
vertexProperties.Cast<dynamic>().ToArray());
+                graph.Vertices[id] = vertex;
+                return vertex;
+            }
+
+            private static Edge AddEdge(Graph graph, object id, Vertex outV, 
string label, Vertex inV,
+                params Property[] properties)
+            {
+                var edge = new Edge(id, outV, label, inV, 
properties.Cast<dynamic>().ToArray());
+                graph.Edges[id] = edge;
+                return edge;
+            }
+        }
     }
 }
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
index 8622316b49..f1e25c3507 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
@@ -22,9 +22,17 @@
 #endregion
 
 using System;
+using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Threading.Tasks;
 using Gremlin.Net.Structure.IO.GraphBinary4;
+using GremlinEdge = Gremlin.Net.Structure.Edge;
+using GremlinGraph = Gremlin.Net.Structure.Graph;
+using GremlinPath = Gremlin.Net.Structure.Path;
+using GremlinProperty = Gremlin.Net.Structure.Property;
+using GremlinVertex = Gremlin.Net.Structure.Vertex;
+using GremlinVertexProperty = Gremlin.Net.Structure.VertexProperty;
 using Xunit;
 
 namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
@@ -159,6 +167,134 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             };
         }
 
+        private static bool OrderedMapComparator(object? x, object? y)
+        {
+            if (x is not IDictionary<object, object?> expected || y is not 
IDictionary<object, object?> actual)
+                return false;
+            if (expected.Count != actual.Count)
+                return false;
+
+            return expected.Zip(actual).All(pair =>
+                Equals(pair.First.Key, pair.Second.Key) && 
Equals(pair.First.Value, pair.Second.Value));
+        }
+
+        private static bool PathComparator(object? x, object? y)
+        {
+            if (x is not GremlinPath expected || y is not GremlinPath actual)
+                return false;
+            if (!expected.Objects.SequenceEqual(actual.Objects) || 
expected.Labels.Count != actual.Labels.Count)
+                return false;
+
+            for (var ix = 0; ix < expected.Labels.Count; ix++)
+            {
+                if (!expected.Labels[ix].SetEquals(actual.Labels[ix]))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexLabelComparator(object? x, object? y)
+        {
+            if (x is not GremlinVertex expected || y is not GremlinVertex 
actual)
+                return false;
+
+            return expected.Equals(actual) &&
+                   expected.Labels.Count == actual.Labels.Count &&
+                   expected.Labels.All(actual.Labels.Contains);
+        }
+
+        private static bool GraphComparator(object? x, object? y)
+        {
+            if (x is not GremlinGraph expected || y is not GremlinGraph actual)
+                return false;
+            if (expected.Vertices.Count != actual.Vertices.Count || 
expected.Edges.Count != actual.Edges.Count)
+                return false;
+
+            foreach (var pair in expected.Vertices)
+            {
+                if (!actual.Vertices.TryGetValue(pair.Key, out var 
actualVertex) ||
+                    !VertexComparator(pair.Value, actualVertex))
+                    return false;
+            }
+
+            foreach (var pair in expected.Edges)
+            {
+                if (!actual.Edges.TryGetValue(pair.Key, out var actualEdge) || 
!EdgeComparator(pair.Value, actualEdge))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexComparator(GremlinVertex expected, 
GremlinVertex actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   LabelsEqual(expected.Labels, actual.Labels) &&
+                   VertexPropertiesEqual(expected.Properties, 
actual.Properties);
+        }
+
+        private static bool EdgeComparator(GremlinEdge expected, GremlinEdge 
actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   LabelsEqual(expected.Labels, actual.Labels) &&
+                   Equals(expected.OutV.Id, actual.OutV.Id) &&
+                   Equals(expected.InV.Id, actual.InV.Id) &&
+                   PropertiesEqual(expected.Properties, actual.Properties);
+        }
+
+        private static bool VertexPropertiesEqual(dynamic[] expected, 
dynamic[] actual)
+        {
+            if (expected.Length != actual.Length)
+                return false;
+
+            for (var ix = 0; ix < expected.Length; ix++)
+            {
+                if (expected[ix] is not GremlinVertexProperty expectedProperty 
||
+                    actual[ix] is not GremlinVertexProperty actualProperty ||
+                    !VertexPropertyComparator(expectedProperty, 
actualProperty))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexPropertyComparator(GremlinVertexProperty 
expected, GremlinVertexProperty actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   Equals(expected.Value, actual.Value) &&
+                   PropertiesEqual(expected.Properties, actual.Properties);
+        }
+
+        private static bool PropertiesEqual(dynamic[] expected, dynamic[] 
actual)
+        {
+            if (expected.Length != actual.Length)
+                return false;
+
+            for (var ix = 0; ix < expected.Length; ix++)
+            {
+                if (expected[ix] is not GremlinProperty expectedProperty ||
+                    actual[ix] is not GremlinProperty actualProperty ||
+                    !PropertyComparator(expectedProperty, actualProperty))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool PropertyComparator(GremlinProperty expected, 
GremlinProperty actual)
+        {
+            return expected.Key == actual.Key && Equals(expected.Value, 
actual.Value);
+        }
+
+        private static bool LabelsEqual(IReadOnlySet<string> expected, 
IReadOnlySet<string> actual)
+        {
+            return expected.Count == actual.Count && 
expected.All(actual.Contains);
+        }
+
         // BigInteger tests
         [Fact]
         public Task TestPosBigInteger() => Run("pos-biginteger");
@@ -166,6 +302,36 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestNegBigInteger() => Run("neg-biginteger");
 
+        [Fact]
+        public Task TestZeroBigInteger() => Run("zero-biginteger");
+
+        [Fact]
+        public Task TestSignBoundaryPosBigInteger() => 
Run("sign-boundary-pos-biginteger");
+
+        [Fact]
+        public Task TestSignBoundaryNegBigInteger() => 
Run("sign-boundary-neg-biginteger");
+
+        // BigDecimal tests
+        [Fact]
+        public Task TestZeroBigDecimal() => Run("zero-bigdecimal");
+
+        [Fact]
+        public Task TestScaleZeroBigDecimal() => Run("scale-zero-bigdecimal");
+
+        [Fact]
+        public Task TestNegativeScaleBigDecimal() =>
+            RunWriteRead("negative-scale-bigdecimal"); // decimal doesn't 
preserve negative scale for byte-exact writes
+
+        [Fact]
+        public Task TestSmallDecimalBigDecimal() => 
Run("small-decimal-bigdecimal");
+
+        // Provider-defined type tests
+        [Fact]
+        public Task TestUint8PrimitivePdt() => Run("uint8-primitive-pdt");
+
+        [Fact]
+        public Task TestPointCompositePdt() => Run("point-composite-pdt");
+
         // Byte tests
         [Fact]
         public Task TestMinByte() => Run("min-byte");
@@ -235,12 +401,33 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         public Task TestSingleByteChar() => Run("single-byte-char");
 
         [Fact]
-        public Task TestMultiByteChar() => Run("multi-byte-char");
+        public Task TestTwoByteChar() => Run("two-byte-char");
+
+        [Fact]
+        public Task TestThreeByteChar() => Run("three-byte-char");
 
         // Null test
         [Fact]
         public Task TestUnspecifiedNull() => Run("unspecified-null");
 
+        [Fact]
+        public Task TestNullInt() => RunRead("null-int"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullLong() => RunRead("null-long"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullString() => RunRead("null-string"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullList() => RunRead("null-list"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullMap() => RunRead("null-map"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullSet() => RunRead("null-set"); // typed nulls 
deserialize to plain null
+
         // Boolean tests
         [Fact]
         public Task TestTrueBoolean() => Run("true-boolean");
@@ -255,17 +442,30 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestMixedString() => Run("mixed-string");
 
+        [Fact]
+        public Task TestEmptyString() => Run("empty-string");
+
         // BulkSet tests (read only - BulkSet deserialized as List)
         [Fact]
-        public Task TestVarBulkList() => RunRead("var-bulklist");
+        public Task TestVarBulkList() => RunRead("var-bulklist"); // BulkList 
deserializes as a plain List
 
         [Fact]
-        public Task TestEmptyBulkList() => RunRead("empty-bulklist");
+        public Task TestEmptyBulkList() => RunRead("empty-bulklist"); // 
BulkList deserializes as a plain List
 
         // Duration tests
         [Fact]
         public Task TestZeroDuration() => Run("zero-duration");
 
+        [Fact]
+        public Task TestPositiveDuration() => Run("positive-duration");
+
+        [Fact]
+        public Task TestNegativeDuration() => Run("negative-duration");
+
+        [Fact]
+        public Task TestNanosDuration() =>
+            RunWriteRead("nanos-duration"); // TimeSpan truncates sub-tick 
nanoseconds for byte-exact writes
+
         // Edge tests
         [Fact]
         public Task TestTraversalEdge() => RunWriteRead("traversal-edge"); // 
properties aren't serialized
@@ -298,6 +498,10 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestEmptyMap() => Run("empty-map");
 
+        [Fact]
+        public Task TestOrderedStringIntMap() =>
+            RunWriteRead("ordered-string-int-map", OrderedMapComparator); // 
writer doesn't emit the ordered-map value flag
+
         // Path tests
         [Fact]
         public Task TestTraversalPath() => RunWriteRead("traversal-path"); // 
properties written as null not empty list
@@ -305,6 +509,13 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestEmptyPath() => Run("empty-path");
 
+        [Fact]
+        public Task TestPathZeroLabels() => Run("path-zero-labels", 
PathComparator);
+
+        [Fact]
+        public Task TestPathMultipleLabels() =>
+            RunWriteRead("path-multiple-labels", PathComparator); // label set 
ordering isn't guaranteed
+
         [Fact]
         public Task TestPropPath() => RunWriteRead("prop-path"); // properties 
aren't serialized
 
@@ -313,19 +524,16 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         public Task TestTraversalTree() => RunWriteRead("traversal-tree"); // 
vertex properties aren't serialized
 
         [Fact]
-        public Task TestEmptyTree() => TestEmptyTreeRoundTrip();
+        public Task TestEmptyTree() => Run("empty-tree");
 
-        private async Task TestEmptyTreeRoundTrip()
-        {
-            // no .gbin resource exists for an empty tree, so exercise an 
in-memory round trip
-            var empty = 
(Gremlin.Net.Structure.Tree)Model.Entries["empty-tree"]!;
-            using var writeStream = new MemoryStream();
-            await Writer.WriteAsync(empty, writeStream);
-            using var readStream = new MemoryStream(writeStream.ToArray());
-            var roundTripped = await Reader.ReadAsync(readStream);
-            Assert.Equal(empty, roundTripped);
-            Assert.True(((Gremlin.Net.Structure.Tree)roundTripped!).IsLeaf());
-        }
+        [Fact]
+        public Task TestTreeNullKey() => Run("tree-null-key");
+
+        [Fact]
+        public Task TestTreeMixedKeyTypes() => Run("tree-mixed-key-types");
+
+        [Fact]
+        public Task TestTreeDeepNesting() => Run("tree-deep-nesting");
 
         // Property tests
         [Fact]
@@ -362,6 +570,17 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestTraversalVertex() => RunWriteRead("traversal-vertex"); 
// properties aren't serialized
 
+        [Fact]
+        public Task TestTinkerGraph() => Run("tinker-graph", GraphComparator);
+
+        [Fact]
+        public Task TestMultiLabelVertex() =>
+            RunWriteRead("multi-label-vertex", VertexLabelComparator); // 
Vertex equality is id-only
+
+        [Fact]
+        public Task TestEmptyLabelVertex() =>
+            RunWriteRead("empty-label-vertex", VertexLabelComparator); // 
Vertex equality is id-only
+
         // VertexProperty tests
         [Fact]
         public Task TestTraversalVertexProperty() => 
RunWriteRead("traversal-vertexproperty"); // properties aren't serialized
@@ -379,5 +598,18 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         // Direction enum test
         [Fact]
         public Task TestOutDirection() => Run("out-direction");
+
+        // Merge enum tests
+        [Fact]
+        public Task TestMergeOnCreate() => Run("merge-on-create");
+
+        [Fact]
+        public Task TestMergeOnMatch() => Run("merge-on-match");
+
+        [Fact]
+        public Task TestMergeOutV() => Run("merge-out-v");
+
+        [Fact]
+        public Task TestMergeInV() => Run("merge-in-v");
     }
 }
diff --git 
a/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
 
b/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
index b3db2853c9..6ca8500f28 100644
--- 
a/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
+++ 
b/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
@@ -111,7 +111,7 @@ export default class MapSerializer {
     if (value_flag === 0x01) {
       return null;
     }
-    if (value_flag !== 0x00) {
+    if (value_flag !== 0x00 && value_flag !== 0x02) {
       throw new Error(`MapSerializer: unexpected 
{value_flag}=0x${value_flag.toString(16)}`);
     }
     return this.deserializeValue(reader, value_flag, type_code);
diff --git a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js 
b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
index bca8a47d7a..a61bfb566e 100644
--- a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
+++ b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
@@ -124,13 +124,6 @@ function runRead(name, comparator = assertEqual) {
   });
 }
 
-// unimplemented type
-function skip(name, reason) {
-  describe(name, () => {
-    it.skip(`${reason}`, () => {});
-  });
-}
-
 function assertEqual(actual, expected) {
   if (Number.isNaN(expected) && Number.isNaN(actual)) return;
   if (Object.is(expected, -0) && Object.is(actual, -0)) return;
@@ -165,6 +158,89 @@ function setComparator(actual, expected) {
   assert.deepStrictEqual([...actual].sort(), [...expected].sort());
 }
 
+function orderedMapComparator(actual, expected) {
+  assert.instanceOf(actual, Map);
+  assert.instanceOf(expected, Map);
+  assert.deepStrictEqual([...actual.entries()], [...expected.entries()]);
+}
+
+function treeComparator(actual, expected) {
+  assert.isTrue(actual.equals(expected));
+}
+
+function pathComparator(actual, expected) {
+  assert.deepStrictEqual(actual.objects, expected.objects);
+  assert.strictEqual(actual.labels.length, expected.labels.length);
+  for (let ix = 0; ix < actual.labels.length; ix++) {
+    setComparator(actual.labels[ix], expected.labels[ix]);
+  }
+}
+
+function vertexLabelComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  setComparator(actual.labels, expected.labels);
+}
+
+function propertyComparator(actual, expected) {
+  assert.strictEqual(actual.key, expected.key);
+  assert.deepStrictEqual(actual.value, expected.value);
+}
+
+function vertexPropertyComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  assert.deepStrictEqual(actual.value, expected.value);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    propertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function vertexComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  setComparator(actual.labels, expected.labels);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    vertexPropertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function edgeComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  setComparator(actual.labels, expected.labels);
+  assert.strictEqual(actual.outV.id, expected.outV.id);
+  assert.strictEqual(actual.inV.id, expected.inV.id);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    propertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function graphComparator(actual, expected) {
+  assert.strictEqual(actual.vertices.size, expected.vertices.size);
+  assert.strictEqual(actual.edges.size, expected.edges.size);
+  assert.deepStrictEqual([...actual.vertices.keys()], 
[...expected.vertices.keys()]);
+  assert.deepStrictEqual([...actual.edges.keys()], [...expected.edges.keys()]);
+  for (const [id, expectedVertex] of expected.vertices) {
+    vertexComparator(actual.vertices.get(id), expectedVertex);
+  }
+  for (const [id, expectedEdge] of expected.edges) {
+    edgeComparator(actual.edges.get(id), expectedEdge);
+  }
+}
+
+function primitivePdtComparator(actual, expected) {
+  assert.strictEqual(actual.name, expected.name);
+  assert.strictEqual(actual.value, expected.value);
+}
+
+function compositePdtComparator(actual, expected) {
+  assert.strictEqual(actual.name, expected.name);
+  assert.deepStrictEqual(actual.fields, expected.fields);
+}
+
 function setCardinalityComparator(actual, expected) {
   assert.strictEqual(actual.id, expected.id);
   assert.strictEqual(actual.label, expected.label);
@@ -182,9 +258,14 @@ function invalidDateComparator(actual, expected) {
 }
 
 describe('GraphBinary v4 Model Tests', () => {
-  // run mode (31 entries)
+  // run mode
   run('pos-biginteger');
   run('neg-biginteger');
+  run('zero-biginteger');
+  run('sign-boundary-pos-biginteger');
+  run('sign-boundary-neg-biginteger');
+  run('uint8-primitive-pdt', primitivePdtComparator);
+  run('point-composite-pdt', compositePdtComparator);
   run('empty-binary');
   run('str-binary');
   run('max-double');
@@ -199,6 +280,7 @@ describe('GraphBinary v4 Model Tests', () => {
   run('false-boolean');
   run('single-byte-string');
   run('mixed-string');
+  run('empty-string');
   run('var-type-list');
   run('empty-list');
   run('no-prop-edge');
@@ -207,15 +289,26 @@ describe('GraphBinary v4 Model Tests', () => {
   run('empty-map');
   run('traversal-path');
   run('empty-path');
+  run('path-zero-labels', pathComparator);
+  run('empty-tree', treeComparator);
+  run('tree-null-key', treeComparator);
+  run('tree-mixed-key-types', treeComparator);
+  run('tree-deep-nesting', treeComparator);
   run('edge-property');
   run('null-property');
   run('empty-set');
   run('no-prop-vertex');
+  run('multi-label-vertex', vertexLabelComparator);
+  run('empty-label-vertex', vertexLabelComparator);
   run('id-t');
   run('out-direction');
+  run('merge-on-create');
+  run('merge-on-match');
+  run('merge-out-v');
+  run('merge-in-v');
   run('neg-zero-double', negZeroComparator);
 
-  // runWriteRead mode (23 entries)
+  // runWriteRead mode
   runWriteRead('min-byte');
   runWriteRead('max-byte');
   runWriteRead('max-float');
@@ -239,35 +332,29 @@ describe('GraphBinary v4 Model Tests', () => {
   runWriteRead('meta-vertexproperty');
   runWriteRead('set-cardinality-vertexproperty', setCardinalityComparator);
   runWriteRead('traversal-vertex');
+  // JS deserializes safe Long values to Number, so vertex-property ids can't 
be re-emitted byte-exactly.
+  runWriteRead('tinker-graph', graphComparator);
+  // label set ordering isn't guaranteed after a JS object round trip.
+  runWriteRead('path-multiple-labels', pathComparator);
+  // JS preserves Map entry order, but its writer doesn't emit the ordered-map 
value flag.
+  runWriteRead('ordered-string-int-map', orderedMapComparator);
+  runWriteRead('traversal-tree', treeComparator); // vertex properties aren't 
serialized
 
-  // Tree
-  runWriteRead('traversal-tree'); // vertex properties aren't serialized
-
-  // empty-tree has no .gbin resource, so exercise an in-memory round trip
-  describe('empty-tree', () => {
-    it('in-memory round trip', async () => {
-      const empty = model['empty-tree'];
-      const roundTripped = await anySerializer.deserialize(
-        StreamReader.fromBuffer(anySerializer.serialize(empty)),
-      );
-      assert.deepStrictEqual(roundTripped, empty);
-      assert.isTrue(roundTripped.isLeaf());
-    });
-  });
-
-  // runRead mode (5 entries)
+  // runRead mode
+  // JS Number can't re-emit this as a Float.
   runRead('neg-zero-float', negZeroComparator);
+  // invalid Date models can't be serialized back.
   runRead('max-offsetdatetime', invalidDateComparator);
   runRead('min-offsetdatetime', invalidDateComparator);
+  // properties aren't serialized in JS for this path fixture.
   runRead('prop-path');
+  // this fixture has complex keys that JS can't write back byte-exactly.
   runRead('var-type-map');
-
-  // skip mode (7 entries)
-  skip('single-byte-char', 'Char type (0x80) not implemented');
-  skip('multi-byte-char', 'Char type (0x80) not implemented');
-  skip('tinker-graph', 'Graph type not implemented');
-  skip('pos-bigdecimal', 'BigDecimal type not implemented');
-  skip('neg-bigdecimal', 'BigDecimal type not implemented');
-  skip('forever-duration', 'Duration type not implemented');
-  skip('zero-duration', 'Duration type not implemented');
-});
\ No newline at end of file
+  // typed nulls deserialize to plain null, so the original type can't be 
re-emitted.
+  runRead('null-int');
+  runRead('null-long');
+  runRead('null-string');
+  runRead('null-list');
+  runRead('null-map');
+  runRead('null-set');
+});
diff --git a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js 
b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
index 63aa9dbfb3..c06f4c4965 100644
--- a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
+++ b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
@@ -17,15 +17,26 @@
  *  under the License.
  */
 
-import { Vertex, Edge, Property, VertexProperty, Path, Tree } from 
'../../../lib/structure/graph.js';
-import { direction, t } from '../../../lib/process/traversal.js';
+import {
+  Graph,
+  Vertex,
+  Edge,
+  Property,
+  VertexProperty,
+  Path,
+  Tree,
+  CompositePDT,
+  PrimitivePDT
+} from '../../../lib/structure/graph.js';
+import { direction, merge, t } from '../../../lib/process/traversal.js';
 
 /*
  * Unsupported types (no map entries, .gbin files exist but types not 
implemented):
- * - single-byte-char, multi-byte-char (Char 0x80 not implemented)
- * - tinker-graph (Graph type not implemented)
- * - pos-bigdecimal, neg-bigdecimal (BigDecimal type not implemented)
- * - forever-duration, zero-duration (Duration type not implemented)
+ * - single-byte-char, two-byte-char, three-byte-char, four-byte-char (Char 
0x80 not implemented)
+ * - pos-bigdecimal, neg-bigdecimal, zero-bigdecimal, scale-zero-bigdecimal,
+ *   negative-scale-bigdecimal, small-decimal-bigdecimal (BigDecimal type not 
implemented)
+ * - forever-duration, zero-duration, positive-duration, negative-duration,
+ *   nanos-duration (Duration type not implemented)
  */
 
 const model = {};
@@ -33,6 +44,13 @@ const model = {};
 // BigInteger values
 model['pos-biginteger'] = BigInt('123456789987654321123456789987654321');
 model['neg-biginteger'] = BigInt('-123456789987654321123456789987654321');
+model['zero-biginteger'] = 0n;
+model['sign-boundary-pos-biginteger'] = 128n;
+model['sign-boundary-neg-biginteger'] = -129n;
+
+// Provider-defined type values
+model['uint8-primitive-pdt'] = new PrimitivePDT('Uint8', '10');
+model['point-composite-pdt'] = new CompositePDT('Point', { x: 1, y: 2 });
 
 // Byte values (JS has no byte type, deserializes to Number)
 model['min-byte'] = -128;
@@ -64,6 +82,12 @@ model['neg-zero-float'] = -0;
 
 // Null
 model['unspecified-null'] = null;
+model['null-int'] = null;
+model['null-long'] = null;
+model['null-string'] = null;
+model['null-list'] = null;
+model['null-map'] = null;
+model['null-set'] = null;
 
 // Boolean values
 model['true-boolean'] = true;
@@ -72,6 +96,7 @@ model['false-boolean'] = false;
 // String values
 model['single-byte-string'] = 'abc';
 model['mixed-string'] = 'abc\u0391\u0392\u0393';
+model['empty-string'] = '';
 
 // List values
 model['var-bulklist'] = ['marko', 'josh', 'josh'];
@@ -111,6 +136,14 @@ model['var-type-map'] = new Map([
   ['test', 123]
 ]);
 model['empty-map'] = new Map();
+model['ordered-string-int-map'] = new Map([
+  ['delta', 4],
+  ['alpha', 1],
+  ['charlie', 3],
+  ['bravo', 2],
+  ['echo', 5],
+  ['foxtrot', 6]
+]);
 
 // Path values
 model['traversal-path'] = new Path(
@@ -118,6 +151,8 @@ model['traversal-path'] = new Path(
   [new Vertex(1, 'person'), new Vertex(10, 'software'), new Vertex(11, 
'software')]
 );
 model['empty-path'] = new Path([], []);
+model['path-zero-labels'] = new Path([new Set()], ['marko']);
+model['path-multiple-labels'] = new Path([new Set(['a', 'b'])], ['marko']);
 
 // Tree values
 // tree for g.V(10).out().tree(): v[10] -> v[11]
@@ -125,6 +160,16 @@ const traversalTree = new Tree();
 traversalTree.getOrCreateChild(new Vertex(10, 
'software')).getOrCreateChild(new Vertex(11, 'software'));
 model['traversal-tree'] = traversalTree;
 model['empty-tree'] = new Tree();
+const treeNullKey = new Tree();
+treeNullKey.getOrCreateChild(null);
+model['tree-null-key'] = treeNullKey;
+const treeMixedKeyTypes = new Tree();
+treeMixedKeyTypes.getOrCreateChild('name');
+treeMixedKeyTypes.getOrCreateChild(123);
+model['tree-mixed-key-types'] = treeMixedKeyTypes;
+const treeDeepNesting = new Tree();
+treeDeepNesting.getOrCreateChild('root').getOrCreateChild('branch').getOrCreateChild('leaf');
+model['tree-deep-nesting'] = treeDeepNesting;
 
 // Complex path with nested properties
 const propPathVertex = new Vertex(1, 'person', [
@@ -183,6 +228,10 @@ model['set-cardinality-vertexproperty'] = new 
VertexProperty(1, 'person', new Se
 // Enum values
 model['id-t'] = t.id;
 model['out-direction'] = direction.out;
+model['merge-on-create'] = merge.onCreate;
+model['merge-on-match'] = merge.onMatch;
+model['merge-out-v'] = merge.outV;
+model['merge-in-v'] = merge.inV;
 
 // Complex vertex with properties (from .gbin deserialization structure)
 const name = new VertexProperty(0, 'name', 'marko');
@@ -202,9 +251,86 @@ const santaFe = new VertexProperty(9, 'location', 'santa 
fe', [
   new Property('startTime', 2005)
 ]);
 model['traversal-vertex'] = new Vertex(1, 'person', [name, sanDiego, 
santaCruz, brussels, santaFe]);
+model['multi-label-vertex'] = new Vertex(1, ['person', 'employee']);
+model['empty-label-vertex'] = new Vertex(1, 'vertex', [], []);
 
 // DateTime values (invalid dates for overflow cases)
 model['max-offsetdatetime'] = new Date(NaN);  // Year 999999999 overflows JS 
Date
 model['min-offsetdatetime'] = new Date(NaN);  // Year -999999999 overflows JS 
Date
 
-export { model };
\ No newline at end of file
+class CrewGraphFactory {
+  static vertexProperty(id, label, value, metaProperties = []) {
+    return new VertexProperty(
+      id,
+      label,
+      value,
+      metaProperties.map(([key, metaValue]) => new Property(key, metaValue))
+    );
+  }
+
+  static addVertex(graph, id, label, propertySpecs) {
+    const vertex = new Vertex(id, label);
+    for (const spec of propertySpecs) {
+      vertex.properties.push(CrewGraphFactory.vertexProperty(spec[0], spec[1], 
spec[2], spec[3] || []));
+    }
+    graph.vertices.set(id, vertex);
+    return vertex;
+  }
+
+  static addEdge(graph, id, outV, label, inV, properties = []) {
+    const edge = new Edge(id, outV, label, inV, properties.map(([key, value]) 
=> new Property(key, value)));
+    graph.edges.set(id, edge);
+    return edge;
+  }
+
+  static create() {
+    const graph = new Graph();
+    const v1 = CrewGraphFactory.addVertex(graph, 1, 'person', [
+      [0, 'name', 'marko'],
+      [6, 'location', 'san diego', [['startTime', 1997], ['endTime', 2001]]],
+      [7, 'location', 'santa cruz', [['startTime', 2001], ['endTime', 2004]]],
+      [8, 'location', 'brussels', [['startTime', 2004], ['endTime', 2005]]],
+      [9, 'location', 'santa fe', [['startTime', 2005]]]
+    ]);
+    const v7 = CrewGraphFactory.addVertex(graph, 7, 'person', [
+      [1, 'name', 'stephen'],
+      [10, 'location', 'centreville', [['startTime', 1990], ['endTime', 
2000]]],
+      [11, 'location', 'dulles', [['startTime', 2000], ['endTime', 2006]]],
+      [12, 'location', 'purcellville', [['startTime', 2006]]]
+    ]);
+    const v8 = CrewGraphFactory.addVertex(graph, 8, 'person', [
+      [2, 'name', 'matthias'],
+      [13, 'location', 'bremen', [['startTime', 2004], ['endTime', 2007]]],
+      [14, 'location', 'baltimore', [['startTime', 2007], ['endTime', 2011]]],
+      [15, 'location', 'oakland', [['startTime', 2011], ['endTime', 2014]]],
+      [16, 'location', 'seattle', [['startTime', 2014]]]
+    ]);
+    const v9 = CrewGraphFactory.addVertex(graph, 9, 'person', [
+      [3, 'name', 'daniel'],
+      [17, 'location', 'spremberg', [['startTime', 1982], ['endTime', 2005]]],
+      [18, 'location', 'kaiserslautern', [['startTime', 2005], ['endTime', 
2009]]],
+      [19, 'location', 'aachen', [['startTime', 2009]]]
+    ]);
+    const v10 = CrewGraphFactory.addVertex(graph, 10, 'software', [[4, 'name', 
'gremlin']]);
+    const v11 = CrewGraphFactory.addVertex(graph, 11, 'software', [[5, 'name', 
'tinkergraph']]);
+    CrewGraphFactory.addEdge(graph, 13, v1, 'develops', v10, [['since', 
2009]]);
+    CrewGraphFactory.addEdge(graph, 14, v1, 'develops', v11, [['since', 
2010]]);
+    CrewGraphFactory.addEdge(graph, 15, v1, 'uses', v10, [['skill', 4]]);
+    CrewGraphFactory.addEdge(graph, 16, v1, 'uses', v11, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 17, v7, 'develops', v10, [['since', 
2010]]);
+    CrewGraphFactory.addEdge(graph, 18, v7, 'develops', v11, [['since', 
2011]]);
+    CrewGraphFactory.addEdge(graph, 19, v7, 'uses', v10, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 20, v7, 'uses', v11, [['skill', 4]]);
+    CrewGraphFactory.addEdge(graph, 21, v8, 'develops', v10, [['since', 
2012]]);
+    CrewGraphFactory.addEdge(graph, 22, v8, 'uses', v10, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 23, v8, 'uses', v11, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 24, v9, 'uses', v10, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 25, v9, 'uses', v11, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 26, v10, 'traverses', v11);
+    return graph;
+  }
+}
+
+model['tinker-graph'] = CrewGraphFactory.create();
+
+export { model };
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
index 90556996d1..1cf5b0f856 100644
--- 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
+++ 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
@@ -1060,4 +1060,4 @@ class PrimitivePDTIO(_GraphBinaryTypeIO):
     def _read_primitive_pdt(cls, b, r):
         name = r.read_object(b)
         value = r.read_object(b)
-        return PrimitivePDT(name, value)
\ No newline at end of file
+        return PrimitivePDT(name, value)
diff --git a/gremlin-python/src/main/python/tests/unit/structure/io/model.py 
b/gremlin-python/src/main/python/tests/unit/structure/io/model.py
index 38200f5149..8410872d06 100644
--- a/gremlin-python/src/main/python/tests/unit/structure/io/model.py
+++ b/gremlin-python/src/main/python/tests/unit/structure/io/model.py
@@ -19,13 +19,13 @@ under the License.
 
 import datetime
 import uuid
-from gremlin_python.statics import short, long, bigint, BigDecimal, SingleByte
-from gremlin_python.structure.graph import Vertex, Edge, Property, 
VertexProperty, Path, Tree
-from gremlin_python.process.traversal import Direction, T
+from collections import OrderedDict
+from gremlin_python.statics import short, long, bigint, BigDecimal, 
SingleByte, SingleChar
+from gremlin_python.structure.graph import Graph, Vertex, Edge, Property, 
VertexProperty, Path, Tree, CompositePDT, PrimitivePDT
+from gremlin_python.process.traversal import Direction, Merge, T
 
 """
 The following models aren't supported.
-tinker-graph         Graph type not implemented
 max-offsetdatetime   Too large for datetime
 min-offsetdatetime   Too small for datetime
 forever-duration     Too large for duration
@@ -43,8 +43,17 @@ model = {}
 
 model["pos-bigdecimal"] = BigDecimal(33, 123456789987654321123456789987654321)
 model["neg-bigdecimal"] = BigDecimal(33, -123456789987654321123456789987654321)
+model["zero-bigdecimal"] = BigDecimal(0, 0)
+model["scale-zero-bigdecimal"] = BigDecimal(0, 1234)
+model["negative-scale-bigdecimal"] = BigDecimal(-2, 1234)
+model["small-decimal-bigdecimal"] = BigDecimal(2, 1234)
 model["pos-biginteger"] = bigint(123456789987654321123456789987654321)
 model["neg-biginteger"] = bigint(-123456789987654321123456789987654321)
+model["zero-biginteger"] = bigint(0)
+model["sign-boundary-pos-biginteger"] = bigint(128)
+model["sign-boundary-neg-biginteger"] = bigint(-129)
+model["uint8-primitive-pdt"] = PrimitivePDT("Uint8", "10")
+model["point-composite-pdt"] = CompositePDT("Point", {"x": 1, "y": 2})
 model["min-byte"] = SingleByte(-128)
 model["max-byte"] = SingleByte(127)
 model["empty-binary"] = bytes("", "utf8")
@@ -58,15 +67,27 @@ model["pos-inf-double"] = float('inf')
 model["neg-inf-double"] = float('-inf')
 model["neg-zero-double"] = -0.0
 model["single-byte-char"] = 'a'
-model["multi-byte-char"] = '\u03A9'
+model["two-byte-char"] = '\u03A9'
+model["three-byte-char"] = SingleChar('\u20AC')
+model["four-byte-char"] = SingleChar('\U0001F600')
 model["unspecified-null"] = None
+model["null-int"] = None
+model["null-long"] = None
+model["null-string"] = None
+model["null-list"] = None
+model["null-map"] = None
+model["null-set"] = None
 model["true-boolean"] = True
 model["false-boolean"] = False
 model["single-byte-string"] = "abc"
 model["mixed-string"] = "abc\u0391\u0392\u0393"
+model["empty-string"] = ""
 model["var-bulklist"] = ["marko", "josh", "josh"]
 model["empty-bulklist"] = []
 model["zero-duration"] = datetime.timedelta()
+model["positive-duration"] = datetime.timedelta(seconds=123)
+model["negative-duration"] = datetime.timedelta(seconds=-123)
+model["nanos-duration"] = datetime.timedelta(seconds=123, microseconds=456.789)
 model["traversal-edge"] = Edge(
     13,
     Vertex(1, 'person'),
@@ -93,17 +114,37 @@ model["var-type-map"] = {
     None: None
 }
 model["empty-map"] = {}
+model["ordered-string-int-map"] = OrderedDict([
+    ("delta", 4),
+    ("alpha", 1),
+    ("charlie", 3),
+    ("bravo", 2),
+    ("echo", 5),
+    ("foxtrot", 6)
+])
 model["traversal-path"] = Path(
     [set(), set(), set()],
     [Vertex(1, "person"), Vertex(10, "software"), Vertex(11, "software")]
 )
 model["empty-path"] = Path([], [])
+model["path-zero-labels"] = Path([set()], ["marko"])
+model["path-multiple-labels"] = Path([{"a", "b"}], ["marko"])
 
 # tree for g.V(10).out().tree(): v[10] -> v[11]
 _traversal_tree = Tree()
 _traversal_tree.get_or_create_child(Vertex(10, 
"software")).get_or_create_child(Vertex(11, "software"))
 model["traversal-tree"] = _traversal_tree
 model["empty-tree"] = Tree()
+_tree_null_key = Tree()
+_tree_null_key.get_or_create_child(None)
+model["tree-null-key"] = _tree_null_key
+_tree_mixed_key_types = Tree()
+_tree_mixed_key_types.get_or_create_child("name")
+_tree_mixed_key_types.get_or_create_child(123)
+model["tree-mixed-key-types"] = _tree_mixed_key_types
+_tree_deep_nesting = Tree()
+_tree_deep_nesting.get_or_create_child("root").get_or_create_child("branch").get_or_create_child("leaf")
+model["tree-deep-nesting"] = _tree_deep_nesting
 
 model["prop-path"] = Path(
     [set(), set(), set()],
@@ -135,6 +176,10 @@ model["meta-vertexproperty"] = VertexProperty(1, "person", 
"stephen", None, [Pro
 model["set-cardinality-vertexproperty"] = VertexProperty(1, "person", 
{"stephen", "marko"}, None, [Property("a", "b", None)])
 model["id-t"] = T.id
 model["out-direction"] = Direction.OUT
+model["merge-on-create"] = Merge.on_create
+model["merge-on-match"] = Merge.on_match
+model["merge-out-v"] = Merge.out_v
+model["merge-in-v"] = Merge.in_v
 
 santa_fe = VertexProperty(
     9, "location", "santa fe", None,
@@ -158,3 +203,82 @@ model["traversal-vertex"] = Vertex(1, "person", [
     Property("name", name, None), Property("location", [
         san_diego, santa_cruz, brussels, santa_fe
     ], None)])
+model["multi-label-vertex"] = Vertex(1, labels=["person", "employee"])
+model["empty-label-vertex"] = Vertex(1, labels=[])
+
+
+class _CrewGraphFactory:
+    @staticmethod
+    def vertex_property(id, label, value, vertex, meta_properties=None):
+        vp = VertexProperty(id, label, value, vertex)
+        for key, meta_value in meta_properties or []:
+            vp.properties.append(Property(key, meta_value, vp))
+        return vp
+
+    @staticmethod
+    def add_vertex(graph, id, label, property_specs):
+        vertex = Vertex(id, label)
+        for spec in property_specs:
+            vertex.properties.append(
+                _CrewGraphFactory.vertex_property(
+                    spec[0], spec[1], spec[2], vertex, spec[3] if len(spec) > 
3 else []))
+        graph.vertices[id] = vertex
+        return vertex
+
+    @staticmethod
+    def add_edge(graph, id, out_v, label, in_v, properties=None):
+        edge = Edge(id, out_v, label, in_v)
+        for key, value in properties or []:
+            edge.properties.append(Property(key, value, edge))
+        graph.edges[id] = edge
+        return edge
+
+    @staticmethod
+    def create():
+        graph = Graph()
+        v1 = _CrewGraphFactory.add_vertex(graph, 1, "person", [
+            (0, "name", "marko"),
+            (6, "location", "san diego", [("startTime", 1997), ("endTime", 
2001)]),
+            (7, "location", "santa cruz", [("startTime", 2001), ("endTime", 
2004)]),
+            (8, "location", "brussels", [("startTime", 2004), ("endTime", 
2005)]),
+            (9, "location", "santa fe", [("startTime", 2005)])
+        ])
+        v7 = _CrewGraphFactory.add_vertex(graph, 7, "person", [
+            (1, "name", "stephen"),
+            (10, "location", "centreville", [("startTime", 1990), ("endTime", 
2000)]),
+            (11, "location", "dulles", [("startTime", 2000), ("endTime", 
2006)]),
+            (12, "location", "purcellville", [("startTime", 2006)])
+        ])
+        v8 = _CrewGraphFactory.add_vertex(graph, 8, "person", [
+            (2, "name", "matthias"),
+            (13, "location", "bremen", [("startTime", 2004), ("endTime", 
2007)]),
+            (14, "location", "baltimore", [("startTime", 2007), ("endTime", 
2011)]),
+            (15, "location", "oakland", [("startTime", 2011), ("endTime", 
2014)]),
+            (16, "location", "seattle", [("startTime", 2014)])
+        ])
+        v9 = _CrewGraphFactory.add_vertex(graph, 9, "person", [
+            (3, "name", "daniel"),
+            (17, "location", "spremberg", [("startTime", 1982), ("endTime", 
2005)]),
+            (18, "location", "kaiserslautern", [("startTime", 2005), 
("endTime", 2009)]),
+            (19, "location", "aachen", [("startTime", 2009)])
+        ])
+        v10 = _CrewGraphFactory.add_vertex(graph, 10, "software", [(4, "name", 
"gremlin")])
+        v11 = _CrewGraphFactory.add_vertex(graph, 11, "software", [(5, "name", 
"tinkergraph")])
+        _CrewGraphFactory.add_edge(graph, 13, v1, "develops", v10, [("since", 
2009)])
+        _CrewGraphFactory.add_edge(graph, 14, v1, "develops", v11, [("since", 
2010)])
+        _CrewGraphFactory.add_edge(graph, 15, v1, "uses", v10, [("skill", 4)])
+        _CrewGraphFactory.add_edge(graph, 16, v1, "uses", v11, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 17, v7, "develops", v10, [("since", 
2010)])
+        _CrewGraphFactory.add_edge(graph, 18, v7, "develops", v11, [("since", 
2011)])
+        _CrewGraphFactory.add_edge(graph, 19, v7, "uses", v10, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 20, v7, "uses", v11, [("skill", 4)])
+        _CrewGraphFactory.add_edge(graph, 21, v8, "develops", v10, [("since", 
2012)])
+        _CrewGraphFactory.add_edge(graph, 22, v8, "uses", v10, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 23, v8, "uses", v11, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 24, v9, "uses", v10, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 25, v9, "uses", v11, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 26, v10, "traverses", v11)
+        return graph
+
+
+model["tinker-graph"] = _CrewGraphFactory.create()
diff --git 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
index 0e4935a916..dc8ed2a34f 100644
--- 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
+++ 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
@@ -38,6 +38,37 @@ test_resource_directory = 
os.environ.get('IO_TEST_DIRECTORY', default_dir + grem
 writer = GraphBinaryWriter()
 reader = GraphBinaryReader()
 
+def decimal_cmp(x, y):
+    return x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value
+
+def vertex_label_cmp(x, y):
+    return x == y and x.labels == y.labels
+
+def property_cmp(x, y):
+    return x.key == y.key and x.value == y.value
+
+def vertex_property_cmp(x, y):
+    return x == y and x.label == y.label and x.value == y.value and \
+           len(x.properties) == len(y.properties) and \
+           all(property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def vertex_cmp(x, y):
+    return x == y and x.label == y.label and x.labels == y.labels and \
+           len(x.properties) == len(y.properties) and \
+           all(vertex_property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def edge_cmp(x, y):
+    return x == y and x.label == y.label and x.labels == y.labels and \
+           x.outV == y.outV and x.inV == y.inV and \
+           len(x.properties) == len(y.properties) and \
+           all(property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def graph_cmp(x, y):
+    return x.vertices.keys() == y.vertices.keys() and \
+           x.edges.keys() == y.edges.keys() and \
+           all(vertex_cmp(x.vertices[k], y.vertices[k]) for k in 
x.vertices.keys()) and \
+           all(edge_cmp(x.edges[k], y.edges[k]) for k in x.edges.keys())
+
 def get_entry(title):
     return model[title]
 
@@ -47,23 +78,29 @@ def read_file_by_name(resource_name):
         return bytearray(resource_file.read())
 
 def test_pos_bigdecimal():
-    def decimal_cmp(x, y):
-        if x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value:
-            return True
-        else:
-            return False
-
+    # BigDecimal writes through Python BigInteger, which may not preserve 
byte-exact sign padding.
     run_writeread("pos-bigdecimal", decimal_cmp)
 
 def test_neg_bigdecimal():
-    def decimal_cmp(x, y):
-        if x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value:
-            return True
-        else:
-            return False
-
+    # BigDecimal writes through Python BigInteger, which may not preserve 
byte-exact sign padding.
     run_writeread("neg-bigdecimal", decimal_cmp)
 
+def test_zero_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("zero-bigdecimal", decimal_cmp)
+
+def test_scale_zero_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("scale-zero-bigdecimal", decimal_cmp)
+
+def test_negative_scale_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("negative-scale-bigdecimal", decimal_cmp)
+
+def test_small_decimal_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("small-decimal-bigdecimal", decimal_cmp)
+
 def test_pos_biginteger():
     # gremlin-python adds an extra 0 byte to the value.
     run_writeread("pos-biginteger")
@@ -72,6 +109,25 @@ def test_neg_biginteger():
     # gremlin-python adds an extra 0 byte to the value.
     run_writeread("neg-biginteger")
 
+def test_zero_biginteger():
+    # gremlin-python may add an extra sign-padding byte to the value.
+    run_writeread("zero-biginteger")
+
+def test_sign_boundary_pos_biginteger():
+    # gremlin-python may add an extra sign-padding byte to the value.
+    run_writeread("sign-boundary-pos-biginteger")
+
+def test_sign_boundary_neg_biginteger():
+    # gremlin-python can't serialize -129 because BigIntIO computes a byte 
length
+    # that is too small for signed two's-complement encoding.
+    run_read("sign-boundary-neg-biginteger")
+
+def test_uint8_primitive_pdt():
+    run("uint8-primitive-pdt")
+
+def test_point_composite_pdt():
+    run("point-composite-pdt")
+
 def test_min_byte():
     run("min-byte")
     
@@ -116,6 +172,17 @@ def test_neg_zero_double():
 def test_zero_duration():
     run("zero-duration")
 
+def test_positive_duration():
+    run("positive-duration")
+
+def test_negative_duration():
+    # timedelta.seconds normalizes negative values, so the writer can't 
preserve the wire value.
+    run_read("negative-duration")
+
+def test_nanos_duration():
+    # Python timedelta only preserves microseconds, not arbitrary nanoseconds.
+    run_read("nanos-duration")
+
 def test_traversal_edge():
     # properties aren't serialized in gremlin-python
     run_writeread("traversal-edge")
@@ -151,6 +218,9 @@ def test_var_type_map():
 def test_empty_map():
     run("empty-map")
 
+def test_ordered_string_int_map():
+    run("ordered-string-int-map")
+
 def test_traversal_path():
     # gremlin-python serializes/deserializes "null" for props not empty list
     run_writeread("traversal-path")
@@ -158,19 +228,29 @@ def test_traversal_path():
 def test_empty_path():
     run("empty-path")
 
+def test_path_zero_labels():
+    run("path-zero-labels")
+
+def test_path_multiple_labels():
+    # label set ordering isn't guaranteed after a Python object round trip.
+    run_writeread("path-multiple-labels")
+
 def test_traversal_tree():
     # gremlin-python doesn't serialize vertex properties, so the read tree's
     # vertices have no properties; vertex equality is by id so the model 
matches.
     run_writeread("traversal-tree")
 
 def test_empty_tree():
-    # no .gbin resource exists for an empty tree, so exercise an in-memory 
round trip
-    from gremlin_python.structure.graph import Tree
-    empty = model["empty-tree"]
-    round_tripped = reader.read_object(writer.write_object(empty))
-    assert isinstance(round_tripped, Tree)
-    assert empty == round_tripped
-    assert round_tripped.is_leaf()
+    run("empty-tree")
+
+def test_tree_null_key():
+    run("tree-null-key")
+
+def test_tree_mixed_key_types():
+    run("tree-mixed-key-types")
+
+def test_tree_deep_nesting():
+    run("tree-deep-nesting")
 
 def test_prop_path():
     # gremlin-python doesn't serialize properties
@@ -226,23 +306,57 @@ def test_out_direction():
     run("out-direction")
 
 def test_var_bulklist():
+    # BulkList is deserialized as a plain list, so the writer emits a regular 
List.
     run_read("var-bulklist")
 
 def test_empty_bulklist():
+    # BulkList is deserialized as a plain list, so the writer emits a regular 
List.
     run_read("empty-bulklist")
 
 def test_single_byte_char():
     # char is serialized as string
     run_writeread("single-byte-char")
 
-def test_multi_byte_char():
+def test_two_byte_char():
     # char is serialized as string
-    run_writeread("multi-byte-char")
+    run_writeread("two-byte-char")
+
+def test_three_byte_char():
+    # char is serialized as string
+    run_writeread("three-byte-char")
+
+def test_four_byte_char():
+    # char is serialized as string
+    run_writeread("four-byte-char")
 
 def test_unspecified_null():
     # no serializer for plain null
     run_writeread("unspecified-null")
 
+def test_null_int():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-int")
+
+def test_null_long():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-long")
+
+def test_null_string():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-string")
+
+def test_null_list():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-list")
+
+def test_null_map():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-map")
+
+def test_null_set():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-set")
+
 def test_true_boolean():
     run("true-boolean")
 
@@ -255,6 +369,38 @@ def test_single_byte_string():
 def test_mixed_string():
     run("mixed-string")
 
+def test_empty_string():
+    run("empty-string")
+
+def test_traversal_vertex():
+    # properties aren't serialized in gremlin-python
+    run_writeread("traversal-vertex")
+
+def test_multi_label_vertex():
+    # Vertex equality is id-only, so compare labels explicitly.
+    run_writeread("multi-label-vertex", vertex_label_cmp)
+
+def test_empty_label_vertex():
+    # Vertex equality is id-only, so compare labels explicitly.
+    run_writeread("empty-label-vertex", vertex_label_cmp)
+
+def test_tinker_graph():
+    # The hand-built graph is structurally equivalent, but it doesn't preserve
+    # every wire-level numeric type tag needed for byte-exact writes.
+    run_writeread("tinker-graph", graph_cmp)
+
+def test_merge_on_create():
+    run("merge-on-create")
+
+def test_merge_on_match():
+    run("merge-on-match")
+
+def test_merge_out_v():
+    run("merge-out-v")
+
+def test_merge_in_v():
+    run("merge-in-v")
+
 def run(resource_name, comparator = None):
     """
     Runs the regular set of tests for the type which is
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
new file mode 100644
index 0000000000..eae30101be
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
new file mode 100644
index 0000000000..f8287f3da7
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
new file mode 100644
index 0000000000..4be0227de7
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
new file mode 100644
index 0000000000..ed5831f36f
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
new file mode 100644
index 0000000000..7998e4009f
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
new file mode 100644
index 0000000000..af8f22e257
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
new file mode 100644
index 0000000000..e31b45bfb2
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
new file mode 100644
index 0000000000..fafab80fb0
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
new file mode 100644
index 0000000000..f18f8fbbbd
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
new file mode 100644
index 0000000000..e73df638e5
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
new file mode 100644
index 0000000000..16d08d701a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..9cb1daf9e2
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
new file mode 100644
index 0000000000..40fdece9d2
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
new file mode 100644
index 0000000000..93938cf057
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
@@ -0,0 +1 @@
+       
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
new file mode 100644
index 0000000000..03afaa5de0
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
new file mode 100644
index 0000000000..bbe779818e
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
new file mode 100644
index 0000000000..d67137bfd2
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
new file mode 100644
index 0000000000..d8d3825962
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
new file mode 100644
index 0000000000..1e8097ca81
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
new file mode 100644
index 0000000000..a01de6154a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
new file mode 100644
index 0000000000..a456ed2bb5
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
new file mode 100644
index 0000000000..3c5c3a5eb4
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
new file mode 100644
index 0000000000..11b750d97a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..18bbdc206b
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
new file mode 100644
index 0000000000..fbf5e60d4e
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
new file mode 100644
index 0000000000..afcf9b041e
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..321d4cfc1c
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
new file mode 100644
index 0000000000..e4927c407b
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
new file mode 100644
index 0000000000..72b0f37865
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
new file mode 100644
index 0000000000..40f0ec93ea
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
new file mode 100644
index 0000000000..0d7f35dfa4
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/two-byte-char-v4.gbin
similarity index 100%
rename from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-byte-char-v4.gbin
rename to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/two-byte-char-v4.gbin
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
new file mode 100644
index 0000000000..eb81eea59c
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..1e3af93198
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
new file mode 100644
index 0000000000..8cd75a9567
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
new file mode 100644
index 0000000000..0d729d3637
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
@@ -0,0 +1,10 @@
+{
+  "@type" : "g:Vertex",
+  "@value" : {
+    "id" : {
+      "@type" : "g:Int32",
+      "@value" : 1
+    },
+    "label" : [ ]
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
new file mode 100644
index 0000000000..3cc762b550
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
@@ -0,0 +1 @@
+""
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
new file mode 100644
index 0000000000..3f56a2d5e1
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "inV"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
new file mode 100644
index 0000000000..b1f1094b4d
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "onCreate"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
new file mode 100644
index 0000000000..330dea4040
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "onMatch"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
new file mode 100644
index 0000000000..fa6342fd4e
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "outV"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
new file mode 100644
index 0000000000..a568881135
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
@@ -0,0 +1,10 @@
+{
+  "@type" : "g:Vertex",
+  "@value" : {
+    "id" : {
+      "@type" : "g:Int32",
+      "@value" : 1
+    },
+    "label" : [ "person", "employee" ]
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
new file mode 100644
index 0000000000..9a7c0a6eac
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT2M3.000456789S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
new file mode 100644
index 0000000000..ca738da37b
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT-2M-3S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
new file mode 100644
index 0000000000..e4e57921ae
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 1.234E+5
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
new file mode 100644
index 0000000000..fa7cc60b6a
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
@@ -0,0 +1,22 @@
+{
+  "@type" : "g:Map",
+  "@value" : [ "delta", {
+    "@type" : "g:Int32",
+    "@value" : 4
+  }, "alpha", {
+    "@type" : "g:Int32",
+    "@value" : 1
+  }, "charlie", {
+    "@type" : "g:Int32",
+    "@value" : 3
+  }, "bravo", {
+    "@type" : "g:Int32",
+    "@value" : 2
+  }, "echo", {
+    "@type" : "g:Int32",
+    "@value" : 5
+  }, "foxtrot", {
+    "@type" : "g:Int32",
+    "@value" : 6
+  } ]
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
new file mode 100644
index 0000000000..f1fc5c5f95
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:Path",
+  "@value" : {
+    "labels" : {
+      "@type" : "g:List",
+      "@value" : [ {
+        "@type" : "g:Set",
+        "@value" : [ "a", "b" ]
+      } ]
+    },
+    "objects" : {
+      "@type" : "g:List",
+      "@value" : [ "marko" ]
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
new file mode 100644
index 0000000000..055a9573de
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:Path",
+  "@value" : {
+    "labels" : {
+      "@type" : "g:List",
+      "@value" : [ {
+        "@type" : "g:Set",
+        "@value" : [ ]
+      } ]
+    },
+    "objects" : {
+      "@type" : "g:List",
+      "@value" : [ "marko" ]
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
new file mode 100644
index 0000000000..39f2b188a8
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:CompositePdt",
+  "@value" : {
+    "type" : "Point",
+    "fields" : {
+      "@type" : "g:Map",
+      "@value" : [ "x", {
+        "@type" : "g:Int32",
+        "@value" : 1
+      }, "y", {
+        "@type" : "g:Int32",
+        "@value" : 2
+      } ]
+    }
+  }
+}
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
new file mode 100644
index 0000000000..9d25f26d45
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT2M3S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
new file mode 100644
index 0000000000..248f75b08b
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 1234
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
new file mode 100644
index 0000000000..ac8603df5f
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : -129
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
new file mode 100644
index 0000000000..adb4a52396
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : 128
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
new file mode 100644
index 0000000000..ff1cd1e182
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 12.34
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
similarity index 54%
copy from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
copy to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
index 46acefff3e..df42204445 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
@@ -1,4 +1,4 @@
 {
   "@type" : "g:Char",
-  "@value" : "Ω"
+  "@value" : "€"
 }
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/two-byte-char-v4.json
similarity index 100%
rename from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
rename to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/two-byte-char-v4.json
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
new file mode 100644
index 0000000000..ae994ba0c3
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
@@ -0,0 +1,7 @@
+{
+  "@type" : "g:PrimitivePdt",
+  "@value" : {
+    "type" : "Uint8",
+    "value" : "10"
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
new file mode 100644
index 0000000000..ef5815d110
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 0
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
new file mode 100644
index 0000000000..4c719fdb15
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : 0
+}
\ No newline at end of file
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
index 745a65fba3..a986c1b2be 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.structure.io;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
@@ -41,6 +42,8 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import 
org.apache.tinkerpop.gremlin.structure.io.graphbinary.GraphBinaryCompatibilityTest;
 import 
org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTypedCompatibilityTest;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDT;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDT;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
@@ -72,6 +75,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -115,6 +119,57 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteZeroBigDecimal() throws Exception {
+        final String resourceName = "zero-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteScaleZeroBigDecimal() throws Exception {
+        final String resourceName = "scale-zero-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNegativeScaleBigDecimal() throws Exception {
+        final String resourceName = "negative-scale-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSmallDecimalBigDecimal() throws Exception {
+        final String resourceName = "small-decimal-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWritePositiveBigInteger() throws Exception {
         final String resourceName = "pos-biginteger";
@@ -141,6 +196,44 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteZeroBigInteger() throws Exception {
+        final String resourceName = "zero-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSignBoundaryPositiveBigInteger() throws 
Exception {
+        final String resourceName = "sign-boundary-pos-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSignBoundaryNegativeBigInteger() throws 
Exception {
+        final String resourceName = "sign-boundary-neg-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMinByte() throws Exception {
         final String resourceName = "min-byte";
@@ -328,6 +421,45 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWritePositiveDuration() throws Exception {
+        final String resourceName = "positive-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNegativeDuration() throws Exception {
+        final String resourceName = "negative-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNanosDuration() throws Exception {
+        final String resourceName = "nanos-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteEdge() throws Exception {
         final String resourceName = "traversal-edge";
@@ -558,6 +690,21 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteOrderedMap() throws Exception {
+        final String resourceName = "ordered-string-int-map";
+
+        final Map resource = findModelEntryObject(resourceName);
+        final Map fromStatic = read(readFromResource(resourceName), Map.class);
+        final Map recycled = read(write(fromStatic, Map.class, resourceName), 
Map.class);
+        assertThat(fromStatic instanceof LinkedHashMap, is(true));
+        assertThat(recycled instanceof LinkedHashMap, is(true));
+        assertEquals(new ArrayList<>(resource.keySet()), new 
ArrayList<>(fromStatic.keySet()));
+        assertEquals(new ArrayList<>(resource.keySet()), new 
ArrayList<>(recycled.keySet()));
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMaxOffsetDateTime() throws Exception {
         final String resourceName = "max-offsetdatetime";
@@ -623,6 +770,32 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWritePathWithZeroLabels() throws Exception {
+        final String resourceName = "path-zero-labels";
+
+        final Path resource = findModelEntryObject(resourceName);
+        final Path fromStatic = read(readFromResource(resourceName), 
Path.class);
+        final Path recycled = read(write(fromStatic, Path.class, 
resourceName), Path.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWritePathWithMultipleLabels() throws Exception {
+        final String resourceName = "path-multiple-labels";
+
+        final Path resource = findModelEntryObject(resourceName);
+        final Path fromStatic = read(readFromResource(resourceName), 
Path.class);
+        final Path recycled = read(write(fromStatic, Path.class, 
resourceName), Path.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteEdgeProperty() throws Exception {
         final String resourceName = "edge-property";
@@ -763,6 +936,34 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertVertex(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteMultiLabelVertex() throws Exception {
+        final String resourceName = "multi-label-vertex";
+
+        final Vertex resource = findModelEntryObject(resourceName);
+        final Vertex fromStatic = read(readFromResource(resourceName), 
Vertex.class);
+        final Vertex recycled = read(write(fromStatic, Vertex.class, 
resourceName), Vertex.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(resource.id(), fromStatic.id());
+        assertEquals(resource.id(), recycled.id());
+        assertEquals(resource.labels(), fromStatic.labels());
+        assertEquals(resource.labels(), recycled.labels());
+    }
+
+    @Test
+    public void shouldReadWriteEmptyLabelVertex() throws Exception {
+        final String resourceName = "empty-label-vertex";
+
+        final Vertex resource = findModelEntryObject(resourceName);
+        final Vertex fromStatic = read(readFromResource(resourceName), 
Vertex.class);
+        final Vertex recycled = read(write(fromStatic, Vertex.class, 
resourceName), Vertex.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(resource.id(), fromStatic.id());
+        assertEquals(resource.id(), recycled.id());
+        assertEquals(resource.labels(), fromStatic.labels());
+        assertEquals(resource.labels(), recycled.labels());
+    }
+
     @Test
     public void shouldReadWriteVertexProperty() throws Exception {
         final String resourceName = "traversal-vertexproperty";
@@ -832,6 +1033,80 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteMergeOnCreate() throws Exception {
+        final String resourceName = "merge-on-create";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeOnMatch() throws Exception {
+        final String resourceName = "merge-on-match";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeOutV() throws Exception {
+        final String resourceName = "merge-out-v";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeInV() throws Exception {
+        final String resourceName = "merge-in-v";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWritePrimitiveProviderDefinedType() throws Exception 
{
+        final String resourceName = "uint8-primitive-pdt";
+
+        final PrimitivePDT resource = findModelEntryObject(resourceName);
+        final PrimitivePDT fromStatic = read(readFromResource(resourceName), 
PrimitivePDT.class);
+        final PrimitivePDT recycled = read(write(fromStatic, 
PrimitivePDT.class, resourceName), PrimitivePDT.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteCompositeProviderDefinedType() throws Exception 
{
+        final String resourceName = "point-composite-pdt";
+
+        final CompositePDT resource = findModelEntryObject(resourceName);
+        final CompositePDT fromStatic = read(readFromResource(resourceName), 
CompositePDT.class);
+        final CompositePDT recycled = read(write(fromStatic, 
CompositePDT.class, resourceName), CompositePDT.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteVarBulkList() throws Exception {
         final String resourceName = "var-bulklist";
@@ -875,8 +1150,20 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
     }
 
     @Test
-    public void shouldReadWriteMultiByteChar() throws Exception {
-        final String resourceName = "multi-byte-char";
+    public void shouldReadWriteTwoByteChar() throws Exception {
+        final String resourceName = "two-byte-char";
+
+        final Character resource = findModelEntryObject(resourceName);
+        final Character fromStatic = read(readFromResource(resourceName), 
Character.class);
+        final Character recycled = read(write(fromStatic, Character.class, 
resourceName), Character.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteThreeByteChar() throws Exception {
+        final String resourceName = "three-byte-char";
 
         final Character resource = findModelEntryObject(resourceName);
         final Character fromStatic = read(readFromResource(resourceName), 
Character.class);
@@ -934,6 +1221,18 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteEmptyString() throws Exception {
+        final String resourceName = "empty-string";
+
+        final String resource = findModelEntryObject(resourceName);
+        final String fromStatic = read(readFromResource(resourceName), 
String.class);
+        final String recycled = read(write(fromStatic, String.class, 
resourceName), String.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMixedString() throws Exception {
         final String resourceName = "mixed-string";
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
index 864697ce70..ac08ad8313 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
@@ -20,14 +20,22 @@ package org.apache.tinkerpop.gremlin.structure.io;
 
 import org.apache.commons.configuration2.BaseConfiguration;
 import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 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.io.pdt.CompositePDT;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDT;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
 import 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
@@ -48,6 +56,8 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -61,6 +71,9 @@ import static 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFacto
 /**
  * Defines the supported types for IO and the versions (and configurations) to 
which they apply and are tested.
  *
+ * The following GraphBinary model resources aren't supported by the Java 
model:
+ * four-byte-char            Character cannot represent a supplementary 
Unicode code point as a single scalar value
+ *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class Model {
@@ -111,6 +124,14 @@ public class Model {
         map.put(null, null);
         addCoreEntry(map, "var-type-map", "Map is redefined so that to provide 
the ability to allow for non-String keys, which is not possible in JSON.");
         addCoreEntry(Collections.EMPTY_MAP, "empty-map");
+        final Map<Object,Object> orderedMap = new LinkedHashMap<>();
+        orderedMap.put("delta", 4);
+        orderedMap.put("alpha", 1);
+        orderedMap.put("charlie", 3);
+        orderedMap.put("bravo", 2);
+        orderedMap.put("echo", 5);
+        orderedMap.put("foxtrot", 6);
+        addCoreEntry(orderedMap, "ordered-string-int-map");
 
         addCoreEntry(new HashSet<>(Arrays.asList(2, "person", true, null)), 
"var-type-set", "Allows a JSON collection to behave as a Set.");
         addCoreEntry(Collections.EMPTY_SET, "empty-set");
@@ -120,19 +141,42 @@ public class Model {
 
         addCoreEntry(Direction.OUT, "out-direction", "");
         addCoreEntry(T.id, "id-t", "");
+        addCoreEntry(Merge.onCreate, "merge-on-create", "");
+        addCoreEntry(Merge.onMatch, "merge-on-match", "");
+        addCoreEntry(Merge.outV, "merge-out-v", "");
+        addCoreEntry(Merge.inV, "merge-in-v", "");
 
         addCoreEntry('a', "single-byte-char", "");
-        addCoreEntry('\u03A9', "multi-byte-char", "");
+        addCoreEntry('\u03A9', "two-byte-char", "");
+        addCoreEntry('\u20AC', "three-byte-char", "");
 
         addEntry("Core", () -> null, "unspecified-null", "");
+        addEntry("Typed Null", () -> null, "null-int", "");
+        addEntry("Typed Null", () -> null, "null-long", "");
+        addEntry("Typed Null", () -> null, "null-string", "");
+        addEntry("Typed Null", () -> null, "null-list", "");
+        addEntry("Typed Null", () -> null, "null-map", "");
+        addEntry("Typed Null", () -> null, "null-set", "");
 
         addCoreEntry(true, "true-boolean", "");
         addCoreEntry(false, "false-boolean", "");
 
         addCoreEntry("abc", "single-byte-string", "");
         addCoreEntry("abc\u0391\u0392\u0393", "mixed-string", "");
+        addCoreEntry("", "empty-string", "");
 
         addCoreEntry(g.V(10).out().tree().next(), "traversal-tree", "");
+        addCoreEntry(new Tree<>(), "empty-tree", "");
+        final Tree<Object> nullKeyTree = new Tree<>();
+        nullKeyTree.getOrCreateChild(null);
+        addCoreEntry(nullKeyTree, "tree-null-key", "");
+        final Tree<Object> mixedKeyTree = new Tree<>();
+        mixedKeyTree.getOrCreateChild("name");
+        mixedKeyTree.getOrCreateChild(123);
+        addCoreEntry(mixedKeyTree, "tree-mixed-key-types", "");
+        final Tree<Object> deepTree = new Tree<>();
+        
deepTree.getOrCreateChild("root").getOrCreateChild("branch").getOrCreateChild("leaf");
+        addCoreEntry(deepTree, "tree-deep-nesting", "");
 
         addGraphStructureEntry(graph.edges().next(), "traversal-edge", "");
         addGraphStructureEntry(DetachedFactory.detach(graph.edges().next(), 
false), "no-prop-edge", "");
@@ -140,6 +184,10 @@ public class Model {
         addGraphStructureEntry(detach(g.V().out().out().path().next(), false), 
"traversal-path", "");
         addGraphStructureEntry(EmptyPath.instance(), "empty-path", "");
         addGraphStructureEntry(detach(g.V().out().out().path().next(), true), 
"prop-path", "");
+        final Path zeroLabelsPath = MutablePath.make().extend("marko", 
Collections.emptySet());
+        addGraphStructureEntry(zeroLabelsPath, "path-zero-labels", "");
+        final Path multipleLabelsPath = MutablePath.make().extend("marko", new 
LinkedHashSet<>(Arrays.asList("a", "b")));
+        addGraphStructureEntry(multipleLabelsPath, "path-multiple-labels", "");
 
         addGraphStructureEntry(graph.edges().next().properties().next(), 
"edge-property", "");
         addGraphStructureEntry(new DetachedProperty<>("", null), 
"null-property", "");
@@ -148,6 +196,16 @@ public class Model {
 
         addGraphStructureEntry(graph.vertices().next(), "traversal-vertex", 
"");
         addGraphStructureEntry(DetachedFactory.detach(graph.vertices().next(), 
false), "no-prop-vertex", "");
+        final Vertex multiLabelVertex = DetachedVertex.build()
+                .setId(1)
+                .setLabels(new LinkedHashSet<>(Arrays.asList("person", 
"employee")))
+                .create();
+        addGraphStructureEntry(multiLabelVertex, "multi-label-vertex", "");
+        final Vertex emptyLabelVertex = DetachedVertex.build()
+                .setId(1)
+                .setLabels(Collections.emptySet())
+                .create();
+        addGraphStructureEntry(emptyLabelVertex, "empty-label-vertex", "");
 
         addGraphStructureEntry(graph.vertices().next().properties().next(), 
"traversal-vertexproperty", "");
         final Map<String,Object> metaProperties = new HashMap<>();
@@ -166,9 +224,22 @@ public class Model {
 
         addExtendedEntry(new 
BigDecimal("123.456789987654321123456789987654321"), "pos-bigdecimal", "");
         addExtendedEntry(new 
BigDecimal("-123.456789987654321123456789987654321"), "neg-bigdecimal", "");
+        addExtendedEntry(BigDecimal.ZERO, "zero-bigdecimal", "");
+        addExtendedEntry(new BigDecimal(new BigInteger("1234"), 0), 
"scale-zero-bigdecimal", "");
+        addExtendedEntry(new BigDecimal(new BigInteger("1234"), -2), 
"negative-scale-bigdecimal", "");
+        addExtendedEntry(new BigDecimal("12.34"), "small-decimal-bigdecimal", 
"");
 
         addExtendedEntry(new 
BigInteger("123456789987654321123456789987654321"), "pos-biginteger", "");
         addExtendedEntry(new 
BigInteger("-123456789987654321123456789987654321"), "neg-biginteger", "");
+        addExtendedEntry(BigInteger.ZERO, "zero-biginteger", "");
+        addExtendedEntry(BigInteger.valueOf(128), 
"sign-boundary-pos-biginteger", "");
+        addExtendedEntry(BigInteger.valueOf(-129), 
"sign-boundary-neg-biginteger", "");
+
+        addExtendedEntry(new PrimitivePDT("Uint8", "10"), 
"uint8-primitive-pdt", "");
+        final Map<String,Object> pointFields = new LinkedHashMap<>();
+        pointFields.put("x", 1);
+        pointFields.put("y", 2);
+        addExtendedEntry(new CompositePDT("Point", pointFields), 
"point-composite-pdt", "");
 
         addExtendedEntry(Byte.MAX_VALUE, "max-byte", "");
         addExtendedEntry(Byte.MIN_VALUE, "min-byte", "");
@@ -178,6 +249,9 @@ public class Model {
 
         addExtendedEntry(Duration.ZERO, "zero-duration","The following example 
is a zero `Duration`");
         addExtendedEntry(ChronoUnit.FOREVER.getDuration(), 
"forever-duration","");
+        addExtendedEntry(Duration.ofSeconds(123), "positive-duration","");
+        addExtendedEntry(Duration.ofSeconds(-123), "negative-duration","");
+        addExtendedEntry(Duration.ofSeconds(123, 456789), "nanos-duration","");
 
         addExtendedEntry(OffsetDateTime.MAX, "max-offsetdatetime", "");
         addExtendedEntry(OffsetDateTime.MIN, "min-offsetdatetime", "");
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
index 092c1adf6c..7ede7cfab5 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
@@ -21,11 +21,13 @@ package 
org.apache.tinkerpop.gremlin.structure.io.graphbinary;
 import io.netty.buffer.ByteBufAllocator;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import 
org.apache.tinkerpop.gremlin.structure.io.AbstractTypedCompatibilityTest;
 import org.apache.tinkerpop.gremlin.structure.io.Buffer;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
 import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -33,6 +35,12 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -107,6 +115,102 @@ public class GraphBinaryCompatibilityTest extends 
AbstractTypedCompatibilityTest
         }
     }
 
+    @Test
+    public void shouldReadNullInt() throws Exception {
+        final String resourceName = "null-int";
+
+        final Integer fromStatic = read(readFromResource(resourceName), 
Integer.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullLong() throws Exception {
+        final String resourceName = "null-long";
+
+        final Long fromStatic = read(readFromResource(resourceName), 
Long.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullString() throws Exception {
+        final String resourceName = "null-string";
+
+        final String fromStatic = read(readFromResource(resourceName), 
String.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullList() throws Exception {
+        final String resourceName = "null-list";
+
+        final List fromStatic = read(readFromResource(resourceName), 
List.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullMap() throws Exception {
+        final String resourceName = "null-map";
+
+        final Map fromStatic = read(readFromResource(resourceName), Map.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullSet() throws Exception {
+        final String resourceName = "null-set";
+
+        final Set fromStatic = read(readFromResource(resourceName), Set.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadWriteEmptyTree() throws Exception {
+        final String resourceName = "empty-tree";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithNullKey() throws Exception {
+        final String resourceName = "tree-null-key";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithMixedKeyTypes() throws Exception {
+        final String resourceName = "tree-mixed-key-types";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithDeepNesting() throws Exception {
+        final String resourceName = "tree-deep-nesting";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
 //    TODO: revisit
 //    @Override
 //    public void shouldReadWriteAuthenticationChallenge() throws Exception {


Reply via email to