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

xiazcy pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/3.8-dev by this push:
     new ec1f544377 [TINKERPOP-3186] - Align Element.properties type for python 
and javascript (#3242)
ec1f544377 is described below

commit ec1f544377f53382f784f33001e9653c97d48293
Author: Yang Xia <[email protected]>
AuthorDate: Mon Oct 20 17:36:55 2025 -0700

    [TINKERPOP-3186] - Align Element.properties type for python and javascript 
(#3242)
---
 CHANGELOG.asciidoc                                 |  3 +-
 docs/src/upgrade/release-3.8.x.asciidoc            | 72 +++++++++++++++++++--
 .../gremlin-javascript/lib/structure/graph.js      | 28 +++-----
 .../lib/structure/io/type-serializers.js           | 25 +++++++-
 .../test/integration/client-tests.js               | 66 ++++++-------------
 .../test/integration/traversal-test.js             | 15 ++---
 .../test/unit/graphbinary/AnySerializer-test.js    |  2 +-
 .../gremlin-javascript/test/unit/graphson-test.js  | 74 +++++++++++++++-------
 .../test/unit/structure-types-test.js              | 41 ++++++++++++
 .../main/python/gremlin_python/structure/graph.py  |  2 +-
 .../gremlin_python/structure/io/graphsonV2d0.py    |  6 +-
 .../gremlin_python/structure/io/graphsonV3d0.py    |  6 +-
 .../tests/driver/test_driver_remote_connection.py  | 12 ++--
 .../python/tests/structure/io/test_graphsonV2d0.py | 58 ++++++++++++++++-
 .../python/tests/structure/io/test_graphsonV3d0.py | 59 ++++++++++++++++-
 .../src/main/python/tests/structure/test_graph.py  | 23 +++++++
 16 files changed, 369 insertions(+), 123 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index a0bfbcc2d3..888cf44f03 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -29,8 +29,9 @@ This release also includes changes from <<release-3-7-XXX, 
3.7.XXX>>.
 * Added the Air Routes dataset to the set of available samples packaged with 
distributions.
 * Added a minimal distribution for `tinkergraph-gremlin` using the `min` 
classifier that doesn't include the sample datasets.
 * Removed Vertex/ReferenceVertex from grammar. Use vertex id in traversals now 
instead.
-* Renamed `none()` step to `discard()`.
 * Fixed bug where `InlineFilterStrategy` could add an empty `has()`.
+* Normalized python and javascript `Element.properties` to lists.
+* Renamed `none()` step to `discard()`.
 * Repurposed `none()` step as a list filtering step with the signature 
`none(P)`.
 * Modified mathematical operators to prevent overflows in steps such as 
`sum()` and 'sack()' to prefer promotion to the next highest number type.
 * Added `DateTime` ontop of the existing 'datetime' grammar.
diff --git a/docs/src/upgrade/release-3.8.x.asciidoc 
b/docs/src/upgrade/release-3.8.x.asciidoc
index 9e0cd612d1..e8795e8b43 100644
--- a/docs/src/upgrade/release-3.8.x.asciidoc
+++ b/docs/src/upgrade/release-3.8.x.asciidoc
@@ -311,21 +311,85 @@ g.inject("Hello").split("")
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-3083[TINKERPOP-3083]
 
 ==== asString() No Longer Allow Nulls
-The `asString()` step will no longer allow `null` input. An 
`IllegalArgumentException` will be thrown for consistency with all other 
parsing steps (i.e. `asDate()`, `asBool()`, `asNumber()`).
+
+The `asString()` step will no longer allow `null` input. An 
`IllegalArgumentException` will be thrown for consistency
+with all other parsing steps (i.e. `asDate()`, `asBool()`, `asNumber()`).
 
 See: 
link:https://lists.apache.org/thread/q76pgrvhprosb4lty63bnsnbw2ljyl7m[DISCUSS] 
thread
 
-==== Javascript Set Deserialization
+==== Serialization Changes
+
+*Properties on Element Serialization in Python & Javascript*
+
+Element properties handling has been inconsistent across GLVs. 
Previously,`gremlin-python` deserialized empty properties
+as None or array depending on the serializer, while `gremlin-javascript` 
returned properties as objects or arrays, with
+empty properties as empty lists or undefined depending on the serializer.
+
+This inconsistency is now resolved, aligning to how properties are handled in 
Gremlin core and in the Java GLV.
+Both GLVs will deserialize element properties into lists of property objects, 
returning empty lists instead of null values
+for missing properties.
+
+For python, the most notable difference is in graphSON when "tokens" is turned 
on for "materializeProperties". The
+properties returned are no longer `None`, but empty lists. Users should update 
their code accordingly.
+
+For javascript, the change is slightly more extensive, as user should no 
longer expect javascript objects to be returned.
+All properties are returned as lists of Property or VertexProperty objects.
+
+[source,javascript]
+----
+// 3.7 and before:
+g.with_("materializeProperties", "tokens").V(1).next() // skip properties with 
token
+// graphson will return properties as a javascript object, which becomes 
undefined
+Vertex { id: 1, label: 'person', properties: undefined }
+// graphbinary will return properties as empty lists
+Vertex { id: 1, label: 'person', properties: [] }
+
+g.V(1).next() // properties returned
+// graphson will return properties as a javascript object
+Vertex {
+  id: 1,
+  label: 'person',
+  properties: { name: [Array], age: [Array] }
+}
+// graphbinary will return properties as lists of VertexProperty objects
+Vertex {
+  id: 1,
+  label: 'person',
+  properties: [ [VertexProperty], [VertexProperty] ]
+}
+
+// 3.8.0 and newer - properties are always arrays, empty array [] for missing 
properties:
+g.with_("materializeProperties", "tokens").V(1).next() // skip properties with 
token
+// both graphson and graphbinary return
+Vertex { id: 1, label: 'person', properties: [] }
+g.V(1).next()
+// both graphson and graphbinary return
+Vertex {
+  id: 1,
+  label: 'person',
+  properties: [ [VertexProperty], [VertexProperty] ]
+}
+
+----
+
+This change only affects how GLVs deserialize property data in client 
applications. The underlying graph serialization
+formats and server-side behavior remain unchanged.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3186[TINKERPOP-3186]
+
+*Javascript Set Deserialization*
 
 Starting from this version, `gremlin-javascript` will deserialize `Set` data 
into a ECMAScript 2015 Set. Previously,
 these were deserialized into arrays.
 
-==== .NET Byte Serialization Change
+*.NET Byte Serialization Change*
 
 The Gremlin .NET serializers has been updated to correctly handle byte values 
as signed integers to align with the IO
 specification, whereas previously it incorrectly serialized and deserialized 
bytes as unsigned values.
 
-This is a breaking change for .NET applications that rely on byte values. 
Existing applications using byte values should consider switching to `sbyte` 
for signed byte operations or `short` for a wider range of values to maintain 
compatibility.
+This is a breaking change for .NET applications that rely on byte values. 
Existing applications using byte values
+should consider switching to `sbyte` for signed byte operations or `short` for 
a wider range of values to maintain
+compatibility.
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-3161[TINKERPOP-3161]
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js
index 02459bc9be..46f4dd14d7 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js
@@ -46,9 +46,11 @@ class Graph {
 }
 
 class Element {
-  constructor(id, label) {
+  // properties are stored as list of property objects
+  constructor(id, label, properties = []) {
     this.id = id;
     this.label = label;
+    this.properties = properties != null ? properties : [];
   }
 
   /**
@@ -62,9 +64,8 @@ class Element {
 }
 
 class Vertex extends Element {
-  constructor(id, label, properties) {
-    super(id, label);
-    this.properties = properties;
+  constructor(id, label, properties = []) {
+    super(id, label, properties);
   }
 
   toString() {
@@ -73,20 +74,10 @@ class Vertex extends Element {
 }
 
 class Edge extends Element {
-  constructor(id, outV, label, inV, properties) {
-    super(id, label);
+  constructor(id, outV, label, inV, properties = []) {
+    super(id, label, properties);
     this.outV = outV;
     this.inV = inV;
-    this.properties = {};
-    if (properties) {
-      if (Array.isArray(properties)) {
-        // Handle array of Property objects
-        properties.forEach((prop) => (this.properties[prop.key] = prop.value));
-      } else {
-        // Handle object format as before
-        Object.keys(properties).forEach((k) => (this.properties[k] = 
properties[k].value));
-      }
-    }
   }
 
   toString() {
@@ -98,11 +89,10 @@ class Edge extends Element {
 }
 
 class VertexProperty extends Element {
-  constructor(id, label, value, properties) {
-    super(id, label);
+  constructor(id, label, value, properties = []) {
+    super(id, label, properties);
     this.value = value;
     this.key = this.label;
-    this.properties = properties;
   }
 
   toString() {
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
index 22166a6e70..7f5a392fd6 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
@@ -311,10 +311,29 @@ class TraversalStrategySerializer extends TypeSerializer {
   }
 }
 
+/**
+ * Helper function to deserialize properties into arrays of VertexProperty or 
Property according to graphSONV3.
+ */
+function deserializeProperties(value, reader) {
+  let properties = [];
+  if ('properties' in value) {
+    const props = reader.read(value['properties']);
+    if (props !== null && props !== undefined) {
+      properties = Object.entries(props).flatMap(([key, values]) => {
+        if (Array.isArray(values) && values.length > 0 && values[0] instanceof 
g.VertexProperty) {
+          return values; // Flatten VertexProperty arrays
+        }
+        return values instanceof g.Property ? values : new g.Property(key, 
values); // create Property object when needed
+      });
+    }
+  }
+  return properties;
+}
+
 class VertexSerializer extends TypeSerializer {
   deserialize(obj) {
     const value = obj[valueKey];
-    return new g.Vertex(this.reader.read(value['id']), value['label'], 
this.reader.read(value['properties']));
+    return new g.Vertex(this.reader.read(value['id']), value['label'], 
deserializeProperties(value, this.reader));
   }
 
   /** @param {Vertex} item */
@@ -340,7 +359,7 @@ class VertexPropertySerializer extends TypeSerializer {
       this.reader.read(value['id']),
       value['label'],
       this.reader.read(value['value']),
-      this.reader.read(value['properties']),
+      deserializeProperties(value, this.reader),
     );
   }
 }
@@ -360,7 +379,7 @@ class EdgeSerializer extends TypeSerializer {
       new g.Vertex(this.reader.read(value['outV']), 
this.reader.read(value['outVLabel'])),
       value['label'],
       new g.Vertex(this.reader.read(value['inV']), 
this.reader.read(value['inVLabel'])),
-      this.reader.read(value['properties']),
+      deserializeProperties(value, this.reader),
     );
   }
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
index a61701d255..da4c2410cc 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
@@ -84,16 +84,8 @@ describe('Client', function () {
           assert.strictEqual(result.length, 1);
           const vertex = result.first().object;
           assert.ok(vertex instanceof graphModule.Vertex);
-          let age, name
-          if (vertex.properties instanceof Array) {
-            const ageProps = vertex.properties.filter(p => p.key === 'age');
-            const nameProps = vertex.properties.filter(p => p.key === 'name');
-            age = ageProps[0];
-            name = nameProps[0];
-          } else {
-            age = vertex.properties.age[0]
-            name = vertex.properties.name[0]
-          }
+          const age = vertex.properties.find(p => p.key === 'age');
+          const name = vertex.properties.find(p => p.key === 'name');
           assert.ok(age);
           assert.ok(name);
           assert.strictEqual(age.value, 29);
@@ -108,7 +100,7 @@ describe('Client', function () {
           assert.strictEqual(result.length, 1);
           const vertex = result.first().object;
           assert.ok(vertex instanceof graphModule.Vertex);
-          assert.ok(vertex.properties === undefined || 
vertex.properties.length === 0);
+          assert.ok(vertex.properties.length === 0);
         });
     });
 
@@ -119,17 +111,8 @@ describe('Client', function () {
           assert.strictEqual(result.length, 1);
           const vertex = result.first();
           assert.ok(vertex instanceof graphModule.Vertex);
-          // if/then until TINKERPOP-3186
-          let age, name
-          if (vertex.properties instanceof Array) {
-            const ageProps = vertex.properties.filter(p => p.key === 'age');
-            const nameProps = vertex.properties.filter(p => p.key === 'name');
-            age = ageProps[0];
-            name = nameProps[0];
-          } else {
-            age = vertex.properties.age[0]
-            name = vertex.properties.name[0]
-          }
+          const age = vertex.properties.find(p => p.key === 'age');
+          const name = vertex.properties.find(p => p.key === 'name');
           assert.ok(age);
           assert.ok(name);
           assert.strictEqual(age.value, 29);
@@ -145,7 +128,7 @@ describe('Client', function () {
           const edge = result.first();
           assert.ok(edge instanceof graphModule.Edge);
           assert.strictEqual(edge.label, 'knows');
-          assert.strictEqual(edge.properties.weight, 0.5);
+          assert.strictEqual(edge.properties[0].value, 0.5);
           assert.ok(edge.inV);
           assert.ok(edge.outV);
         });
@@ -161,18 +144,12 @@ describe('Client', function () {
           assert.strictEqual(prop.key, 'location');
           assert.strictEqual(prop.value, 'centreville');
 
-          // Check meta-properties - TINKERPOP-3186
-          if (prop.properties instanceof Object && !(prop.properties 
instanceof Array)) {
-            assert.strictEqual(prop.properties.startTime, 1990);
-            assert.strictEqual(prop.properties.endTime, 2000);
-          } else {
-            const startTime = prop.properties.find(p => p.key === 'startTime');
-            const endTime = prop.properties.find(p => p.key === 'endTime');
-            assert.ok(startTime);
-            assert.ok(endTime);
-            assert.strictEqual(startTime.value, 1990);
-            assert.strictEqual(endTime.value, 2000);
-          }
+          const startTime = prop.properties.find(p => p.key === 'startTime');
+          const endTime = prop.properties.find(p => p.key === 'endTime');
+          assert.ok(startTime);
+          assert.ok(endTime);
+          assert.strictEqual(startTime.value, 1990);
+          assert.strictEqual(endTime.value, 2000);
         });
     });
 
@@ -183,7 +160,7 @@ describe('Client', function () {
           assert.strictEqual(result.length, 1);
           const vertex = result.first();
           assert.ok(vertex instanceof graphModule.Vertex);
-          assert.ok(vertex.properties === undefined || 
vertex.properties.length === 0);
+          assert.ok(vertex.properties.length === 0);
         });
     });
 
@@ -355,15 +332,10 @@ function assertVertexProperties(vertex) {
 
   const vertexProperty = locations[0];
   assert.strictEqual(vertexProperty.value, 'centreville');
-  if (vertexProperty.properties instanceof Array) {
-    const start = vertexProperty.properties.find(p => p.key === 'startTime');
-    const end = vertexProperty.properties.find(p => p.key === 'endTime');
-    assert.ok(start);
-    assert.ok(end);
-    assert.strictEqual(start.value, 1990);
-    assert.strictEqual(end.value, 2000);
-  } else {
-    assert.strictEqual(vertexProperty.properties.startTime, 1990);
-    assert.strictEqual(vertexProperty.properties.endTime, 2000);
-  }
+  const start = vertexProperty.properties.find(p => p.key === 'startTime');
+  const end = vertexProperty.properties.find(p => p.key === 'endTime');
+  assert.ok(start);
+  assert.ok(end);
+  assert.strictEqual(start.value, 1990);
+  assert.strictEqual(end.value, 2000);
 }
\ No newline at end of file
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
index 89b4c85232..60cd93bbfa 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
@@ -137,7 +137,7 @@ describe('Traversal', function () {
         assert.ok(list);
         assert.strictEqual(list.length, 6);
         list.forEach(v => assert.ok(v instanceof Vertex));
-        list.forEach(v => assert.ok(v.properties === undefined || 
v.properties.length === 0));
+        list.forEach(v => assert.strictEqual(v.properties.length, 0));
       });
     });
     it('should skip edge properties when tokens is set', function () {
@@ -156,7 +156,7 @@ describe('Traversal', function () {
         assert.ok(list);
         assert.strictEqual(list.length, 12);
         list.forEach(vp => assert.ok(vp instanceof VertexProperty));
-        list.forEach(vp => assert.ok(vp.properties === undefined || 
vp.properties.length === 0));
+        list.forEach(vp => assert.strictEqual(vp.properties.length, 0));
       });
     });
     it('should skip path element properties when tokens is set', function () {
@@ -171,9 +171,9 @@ describe('Traversal', function () {
         assert.ok(a instanceof Vertex);
         assert.ok(b instanceof Edge);
         assert.ok(c instanceof Vertex);
-        assert.ok(a.properties === undefined || a.properties.length === 0);
+        assert.strictEqual(a.properties.length, 0);
         assert.strictEqual(Object.keys(b.properties).length, 0);
-        assert.ok(c.properties === undefined || c.properties.length === 0);
+        assert.strictEqual(c.properties.length, 0);
       });
     });
     it('should materialize path element properties when all is set', function 
() {
@@ -207,12 +207,7 @@ describe('Traversal', function () {
         assert.strictEqual(aAgeProps.length, 1);
         assert.strictEqual(aAgeProps[0].value, 29);
         assert.ok(b.properties);
-        let bWeight;
-        if (b.properties instanceof Array) {
-          bWeight = b.properties.filter(p => p.key === 'weight');
-        } else {
-          bWeight = b.properties['weight'];
-        }
+        const bWeight = b.properties[0].value
         assert.ok(bWeight !== undefined);
         assert.strictEqual(bWeight, 0.4);
         assert.ok(c.properties);
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/AnySerializer-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/AnySerializer-test.js
index d723c85cd9..deb7f5ccf1 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/AnySerializer-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/AnySerializer-test.js
@@ -267,7 +267,7 @@ describe('GraphBinary.AnySerializer', () => {
           0x00,0x00,0x00,0x05, ...from('Label'),
           0x01,0x00, 0x00,0x00,0x00,0x2A,
           0xFE,0x01,
-          0xFE,0x01,
+          0x09,0x00, 0x00,0x00,0x00,0x00,
         ]
       },
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
index 913102b098..8de0173ea6 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
@@ -92,21 +92,23 @@ describe('GraphSONReader', function () {
   });
   it('should parse vertices from GraphSON', function () {
     const obj = {
-      "@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person",
-        
"properties":{"name":[{"id":{"@type":"g:Int64","@value":0},"value":"marko"}],
-          
"age":[{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29}}]}}};
+      
"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person",
+        
"properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},
+              "value":"marko","label":"name"}}],"age":[{
+            
"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},
+              "value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}}
     const reader = new GraphSONReader(obj);
     const result = reader.read(obj);
     assert.ok(result instanceof graph.Vertex);
     assert.strictEqual(result.label, 'person');
     assert.strictEqual(typeof result.id, 'number');
     assert.ok(result.properties);
-    assert.ok(result.properties['name']);
-    assert.strictEqual(result.properties['name'].length, 1);
-    assert.strictEqual(result.properties['name'][0].value, 'marko');
-    assert.ok(result.properties['age']);
-    assert.strictEqual(result.properties['age'].length, 1);
-    assert.strictEqual(result.properties['age'][0].value, 29);
+    assert.ok(result.properties[0]);
+    assert.strictEqual(result.properties[0].key, 'name');
+    assert.strictEqual(result.properties[0].value, 'marko');
+    assert.ok(result.properties[1]);
+    assert.strictEqual(result.properties[1].key, 'age');
+    assert.strictEqual(result.properties[1].value, 29);
   });
   it('should parse paths from GraphSON', function () {
     const obj = 
{"@type":"g:Path","@value":{"labels":[["a"],["b","c"],[]],"objects":[
@@ -136,19 +138,19 @@ describe('GraphSONReader', function () {
     assert.strictEqual(a.label, 'person');
     assert.strictEqual(bc.label, 'software');
     assert.ok(a.properties);
-    assert.ok(a.properties['name']);
-    assert.strictEqual(a.properties['name'].length, 1);
-    assert.strictEqual(a.properties['name'][0].value, 'marko');
-    assert.ok(a.properties['age']);
-    assert.strictEqual(a.properties['age'].length, 1);
-    assert.strictEqual(a.properties['age'][0].value, 29);
+    assert.ok(a.properties[0]);
+    assert.strictEqual(a.properties[0].key, 'name');
+    assert.strictEqual(a.properties[0].value, 'marko');
+    assert.ok(a.properties[1]);
+    assert.strictEqual(a.properties[1].key, 'age');
+    assert.strictEqual(a.properties[1].value, 29);
     assert.ok(bc.properties);
-    assert.ok(bc.properties['name']);
-    assert.strictEqual(bc.properties['name'].length, 1);
-    assert.strictEqual(bc.properties['name'][0].value, 'lop');
-    assert.ok(bc.properties['lang']);
-    assert.strictEqual(bc.properties['lang'].length, 1);
-    assert.strictEqual(bc.properties['lang'][0].value, 'java');
+    assert.ok(bc.properties[0]);
+    assert.strictEqual(bc.properties[0].key, 'name');
+    assert.strictEqual(bc.properties[0].value, 'lop');
+    assert.ok(bc.properties[1]);
+    assert.strictEqual(bc.properties[1].key, 'lang');
+    assert.strictEqual(bc.properties[1].value, 'java');
   });
   it('should parse paths from GraphSON3 without properties', function () {
     const obj = {
@@ -203,8 +205,34 @@ describe('GraphSONReader', function () {
     assert.ok(bc instanceof graph.Vertex);
     assert.strictEqual(a.label, 'person');
     assert.strictEqual(bc.label, 'software');
-    assert.ok(a.properties === undefined);
-    assert.ok(bc.properties === undefined);
+    assert.deepStrictEqual(a.properties, []);
+    assert.deepStrictEqual(bc.properties, []);
+  });
+
+  it('should deserialize vertices without properties to empty array', 
function() {
+    const obj = {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}};
+    const reader = new GraphSONReader();
+    const vertex = reader.read(obj);
+    assert.strictEqual(vertex.constructor.name, 'Vertex');
+    assert.strictEqual(vertex.label, 'person');
+    assert.strictEqual(vertex.id, 2);
+    assert.deepStrictEqual(vertex.properties, []);
+  });
+
+  it('should deserialize edges without properties to empty array', function() {
+    const obj = {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":18},"label":"knows","inV":"a","outV":"b","inVLabel":"xLab"}};
+    const reader = new GraphSONReader();
+    const edge = reader.read(obj);
+    assert.strictEqual(edge.constructor.name, 'Edge');
+    assert.deepStrictEqual(edge.properties, []);
+  });
+
+  it('should deserialize vertex properties without meta-properties to empty 
array', function() {
+    const obj = {"@type":"g:VertexProperty", 
"@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}};
+    const reader = new GraphSONReader();
+    const vp = reader.read(obj);
+    assert.strictEqual(vp.constructor.name, 'VertexProperty');
+    assert.deepStrictEqual(vp.properties, []);
   });
 });
 describe('GraphSONWriter', function () {
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/structure-types-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/structure-types-test.js
index 29bebfad92..f319a656a9 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/structure-types-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/structure-types-test.js
@@ -32,6 +32,18 @@ describe('Edge', () => {
       assert.strictEqual(element.toString(), `e[123][?-label1->?]`);
     });
   });
+
+  describe('properties', () => {
+    it('should default to empty array when not provided', () => {
+      const edge = new Edge('123', new Vertex(1), 'knows', new Vertex(2));
+      assert.deepStrictEqual(edge.properties, []);
+    });
+
+    it('should default to empty array when undefined', () => {
+      const edge = new Edge('123', new Vertex(1), 'knows', new Vertex(2), 
undefined);
+      assert.deepStrictEqual(edge.properties, []);
+    });
+  });
 });
 
 describe('Vertex', () => {
@@ -41,6 +53,23 @@ describe('Vertex', () => {
       assert.strictEqual(element.toString(), `v[-200]`);
     });
   });
+
+  describe('properties', () => {
+    it('should default to empty array when not provided', () => {
+      const vertex = new Vertex(1, 'person');
+      assert.deepStrictEqual(vertex.properties, []);
+    });
+
+    it('should default to empty array when undefined', () => {
+      const vertex = new Vertex(1, 'person', undefined);
+      assert.deepStrictEqual(vertex.properties, []);
+    });
+
+    it('should default to empty array when null', () => {
+      const vertex = new Vertex(1, 'person', null);
+      assert.deepStrictEqual(vertex.properties, []);
+    });
+  });
 });
 
 describe('VertexProperty', () => {
@@ -56,6 +85,18 @@ describe('VertexProperty', () => {
       });
     });
   });
+
+  describe('properties', () => {
+    it('should default to empty array when not provided', () => {
+      const vp = new VertexProperty(24, 'name', 'marko');
+      assert.deepStrictEqual(vp.properties, []);
+    });
+
+    it('should default to empty array when undefined', () => {
+      const vp = new VertexProperty(24, 'name', 'marko', undefined);
+      assert.deepStrictEqual(vp.properties, []);
+    });
+  });
 });
 
 describe('Property', () => {
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/graph.py 
b/gremlin-python/src/main/python/gremlin_python/structure/graph.py
index ab83a538e1..2e3dad2727 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/graph.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/graph.py
@@ -34,7 +34,7 @@ class Element(object):
     def __init__(self, id, label, properties=None):
         self.id = id
         self.label = label
-        self.properties = properties
+        self.properties = [] if properties is None else properties
 
     def __eq__(self, other):
         return isinstance(other, self.__class__) and self.id == other.id
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
index 08c82c5e45..7ebffd01b9 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
@@ -607,7 +607,7 @@ class VertexDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
@@ -620,7 +620,7 @@ class EdgeDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
@@ -637,7 +637,7 @@ class VertexPropertyDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
index 75bc642b26..66a1fb0d7f 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
@@ -704,7 +704,7 @@ class VertexDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
@@ -717,7 +717,7 @@ class EdgeDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
@@ -734,7 +734,7 @@ class VertexPropertyDeserializer(_GraphSONTypeIO):
 
     @classmethod
     def objectify(cls, d, reader):
-        properties = None
+        properties = []
         if "properties" in d:
             properties = reader.to_object(d["properties"])
             if properties is not None:
diff --git 
a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py 
b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
index 346d5ffbb3..80aaa272ef 100644
--- 
a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
+++ 
b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
@@ -116,24 +116,24 @@ class TestDriverRemoteConnection(object):
         # test materializeProperties in V - GraphSON will deserialize into 
None and GraphBinary to []
         results = g.with_("materializeProperties", "tokens").V().to_list()
         for v in results:
-            assert v.properties is None or len(v.properties) == 0
+            assert len(v.properties) == 0
         # #
         # test materializeProperties in E - GraphSON will deserialize into 
None and GraphBinary to []
         results = g.with_("materializeProperties", "tokens").E().to_list()
         for e in results:
-            assert e.properties is None or len(e.properties) == 0
+            assert len(e.properties) == 0
         # #
         # test materializeProperties in VP - GraphSON will deserialize into 
None and GraphBinary to []
         results = g.with_("materializeProperties", 
"tokens").V().properties().to_list()
         for vp in results:
-            assert vp.properties is None or len(vp.properties) == 0
+            assert len(vp.properties) == 0
         # #
         # test materializeProperties in Path - GraphSON will deserialize into 
None and GraphBinary to []
         p = g.with_("materializeProperties", "tokens").V().has('name', 
'marko').outE().inV().has_label('software').path().next()
         assert 3 == len(p.objects)
-        assert p.objects[0].properties is None or len(p.objects[0].properties) 
== 0
-        assert p.objects[1].properties is None or len(p.objects[1].properties) 
== 0
-        assert p.objects[2].properties is None or len(p.objects[2].properties) 
== 0
+        assert len(p.objects[0].properties) == 0
+        assert len(p.objects[1].properties) == 0
+        assert len(p.objects[2].properties) == 0
         # #
         # test materializeProperties in Path - 'all' should materialize 
properties on each element
         p = g.with_("materializeProperties", "all").V().has('name', 
'marko').outE().inV().has_label('software').path().next()
diff --git 
a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py 
b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
index 8b31970605..d6ff7cd7b0 100644
--- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
+++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
@@ -154,13 +154,16 @@ class TestGraphSONReader:
 
     def test_graph(self):
         vertex = self.graphson_reader.read_object("""
-        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","outE":{"created":[{"id":{"@type":"g:Int32","@value":9},"inV":{"@type":"g:Int32","@value":3},"properties":{"weight":{"@type":"g:Double","@value":0.4}}}],"knows":[{"id":{"@type":"g:Int32","@value":7},"inV":{"@type":"g:Int32","@value":2},"properties":{"weight":{"@type":"g:Double","@value":0.5}}},{"id":{"@type":"g:Int32","@value":8},"inV":{"@type":"g:Int32","@value":4},"properties":{"weight":{"@type"
 [...]
+        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}}""")
         assert isinstance(vertex, Vertex)
         assert "person" == vertex.label
         assert 1 == vertex.id
         assert isinstance(vertex.id, int)
         assert vertex == Vertex(1)
         assert 2 == len(vertex.properties)
+        # assert actual property values - Vertex.properties should be 
VertexProperty objects
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
vertex.properties)
+        assert any(vp.label == 'age' and vp.value == 29 for vp in 
vertex.properties)
         ##
         vertex = self.graphson_reader.read_object("""
         {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Float","@value":45.23}}}""")
@@ -169,6 +172,18 @@ class TestGraphSONReader:
         assert isinstance(vertex.id, FloatType)
         assert "vertex" == vertex.label
         assert vertex == Vertex(45.23)
+        # properties key omitted should yield empty list
+        assert vertex.properties == []
+        ##
+        # vertex with explicit label and without properties
+        vertex = self.graphson_reader.read_object(
+            """
+        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}
+            """)
+        assert isinstance(vertex, Vertex)
+        assert vertex.label == 'person'
+        assert vertex.id == 2
+        assert vertex.properties == []
         ##
         vertex_property = self.graphson_reader.read_object("""
         {"@type":"g:VertexProperty", 
"@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""")
@@ -177,6 +192,8 @@ class TestGraphSONReader:
         assert "aKey" == vertex_property.label
         assert vertex_property.value
         assert vertex_property.vertex == Vertex(9)
+        # no properties key should yield empty list of meta-properties
+        assert vertex_property.properties == []
         ##
         vertex_property = self.graphson_reader.read_object("""
         {"@type":"g:VertexProperty", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""")
@@ -185,6 +202,8 @@ class TestGraphSONReader:
         assert "name" == vertex_property.label
         assert "marko" == vertex_property.value
         assert vertex_property.vertex is None
+        # no properties key should yield empty list of meta-properties
+        assert vertex_property.properties == []
         ##
         edge = self.graphson_reader.read_object("""
         {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""")
@@ -195,6 +214,14 @@ class TestGraphSONReader:
         assert edge.inV == Vertex("x", "xLabel")
         assert edge.outV == Vertex("y", "vertex")
         ##
+        # edge without properties should yield empty properties list
+        edge2 = self.graphson_reader.read_object(
+            """
+        {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":18},"label":"knows","inV":"a","outV":"b","inVLabel":"xLab"}}
+            """)
+        assert isinstance(edge2, Edge)
+        assert edge2.properties == []
+        ##
         property = self.graphson_reader.read_object("""
         {"@type":"g:Property", 
"@value":{"key":"aKey","value":{"@type":"g:Int64","@value":17},"element":{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":122},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab"}}}}""")
         # print property
@@ -204,6 +231,7 @@ class TestGraphSONReader:
         assert Edge(122, Vertex("x"), "knows", Vertex("y")) == property.element
 
     def test_path(self):
+        # original path with vertices that include properties
         path = self.graphson_reader.read_object(
             
"""{"@type":"g:Path","@value":{"labels":[["a"],["b","c"],[]],"objects":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32",
 [...]
         )
@@ -213,6 +241,34 @@ class TestGraphSONReader:
         assert Vertex(1) == path["a"]
         assert "lop" == path[2]
         assert 3 == len(path)
+        # ensure element properties were populated from GraphSON
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
path[0].properties)
+        assert any(vp.label == 'age' and vp.value == 29 for vp in 
path[0].properties)
+        assert any(vp.label == 'name' and vp.value == 'lop' for vp in 
path[1].properties)
+        assert any(vp.label == 'lang' and vp.value == 'java' for vp in 
path[1].properties)
+
+        # path with elements that exclude properties (no "properties" key)
+        path2 = self.graphson_reader.read_object(
+            
"""{"@type":"g:Path","@value":{"labels":[["x"],["y"],["z"]],"objects":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":11},"label":"person"}},{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":77},"label":"knows","outV":{"@type":"g:Int32","@value":11},"outVLabel":"person","inV":{"@type":"g:Int32","@value":12},"inVLabel":"person"}},"hello"]}}"""
+        )
+        assert isinstance(path2, Path)
+        assert 3 == len(path2)
+        assert Vertex(11, 'person') == path2[0]
+        assert path2[0].properties == []
+        assert isinstance(path2[1], Edge)
+        assert path2[1].properties == []
+        assert path2[2] == "hello"
+
+        # mixed path: first vertex with properties, second vertex without
+        path3 = self.graphson_reader.read_object(
+            
"""{"@type":"g:Path","@value":{"labels":[["a"],["b"]],"objects":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}]}}"""
+        )
+        assert isinstance(path3, Path)
+        assert 2 == len(path3)
+        assert Vertex(1) == path3[0]
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
path3[0].properties)
+        assert Vertex(2) == path3[1]
+        assert path3[1].properties == []
 
     def test_custom_mapping(self):
 
diff --git 
a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py 
b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
index 099c1ab37c..91c1e6d7a7 100644
--- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
+++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
@@ -199,12 +199,16 @@ class TestGraphSONReader:
 
     def test_graph(self):
         vertex = self.graphson_reader.read_object("""
-        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","outE":{"created":[{"id":{"@type":"g:Int32","@value":9},"inV":{"@type":"g:Int32","@value":3},"properties":{"weight":{"@type":"g:Double","@value":0.4}}}],"knows":[{"id":{"@type":"g:Int32","@value":7},"inV":{"@type":"g:Int32","@value":2},"properties":{"weight":{"@type":"g:Double","@value":0.5}}},{"id":{"@type":"g:Int32","@value":8},"inV":{"@type":"g:Int32","@value":4},"properties":{"weight":{"@type"
 [...]
+        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}}""")
         assert isinstance(vertex, Vertex)
         assert "person" == vertex.label
         assert 1 == vertex.id
         assert isinstance(vertex.id, int)
         assert vertex == Vertex(1)
+        assert 2 == len(vertex.properties)
+        # assert actual property values - Vertex.properties should be 
VertexProperty objects
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
vertex.properties)
+        assert any(vp.label == 'age' and vp.value == 29 for vp in 
vertex.properties)
         ##
         vertex = self.graphson_reader.read_object("""
         {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Float","@value":45.23}}}""")
@@ -213,6 +217,18 @@ class TestGraphSONReader:
         assert isinstance(vertex.id, FloatType)
         assert "vertex" == vertex.label
         assert vertex == Vertex(45.23)
+        # properties key omitted should yield empty list
+        assert vertex.properties == []
+        ##
+        # vertex with explicit label and without properties
+        vertex = self.graphson_reader.read_object(
+            """
+        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}
+            """)
+        assert isinstance(vertex, Vertex)
+        assert vertex.label == 'person'
+        assert vertex.id == 2
+        assert vertex.properties == []
         ##
         vertex_property = self.graphson_reader.read_object("""
         {"@type":"g:VertexProperty", 
"@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""")
@@ -221,6 +237,8 @@ class TestGraphSONReader:
         assert "aKey" == vertex_property.label
         assert vertex_property.value
         assert vertex_property.vertex == Vertex(9)
+        # no properties key should yield empty list of meta-properties
+        assert vertex_property.properties == []
         ##
         vertex_property = self.graphson_reader.read_object("""
         {"@type":"g:VertexProperty", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""")
@@ -229,6 +247,8 @@ class TestGraphSONReader:
         assert "name" == vertex_property.label
         assert "marko" == vertex_property.value
         assert vertex_property.vertex is None
+        # no properties key should yield empty list of meta-properties
+        assert vertex_property.properties == []
         ##
         edge = self.graphson_reader.read_object("""
         {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""")
@@ -239,6 +259,14 @@ class TestGraphSONReader:
         assert edge.inV == Vertex("x", "xLabel")
         assert edge.outV == Vertex("y", "vertex")
         ##
+        # edge without properties should yield empty properties list
+        edge2 = self.graphson_reader.read_object(
+            """
+        {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":18},"label":"knows","inV":"a","outV":"b","inVLabel":"xLab"}}
+            """)
+        assert isinstance(edge2, Edge)
+        assert edge2.properties == []
+        ##
         property = self.graphson_reader.read_object("""
         {"@type":"g:Property", 
"@value":{"key":"aKey","value":{"@type":"g:Int64","@value":17},"element":{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":122},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab"}}}}""")
         # print property
@@ -248,6 +276,7 @@ class TestGraphSONReader:
         assert Edge(122, Vertex("x"), "knows", Vertex("y")) == property.element
 
     def test_path(self):
+        # original path with vertices that include properties
         path = self.graphson_reader.read_object(
             
"""{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["a"]},{"@type":"g:Set","@value":["b","c"]},{"@type":"g:Set","@value":[]}]},"objects":{"@type":"g:List","@value":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":
 [...]
         )
@@ -257,6 +286,34 @@ class TestGraphSONReader:
         assert Vertex(1) == path["a"]
         assert "lop" == path[2]
         assert 3 == len(path)
+        # ensure element properties were populated from GraphSON
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
path[0].properties)
+        assert any(vp.label == 'age' and vp.value == 29 for vp in 
path[0].properties)
+        assert any(vp.label == 'name' and vp.value == 'lop' for vp in 
path[1].properties)
+        assert any(vp.label == 'lang' and vp.value == 'java' for vp in 
path[1].properties)
+
+        # path with elements that exclude properties (no "properties" key)
+        path2 = self.graphson_reader.read_object(
+            
"""{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["x"]},{"@type":"g:Set","@value":["y"]},{"@type":"g:Set","@value":["z"]}]},"objects":{"@type":"g:List","@value":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":11},"label":"person"}},{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":77},"label":"knows","outV":{"@type":"g:Int32","@value":11},"outVLabel":"person","inV":{"@type":"g:Int32","@value":12},"inVLabe
 [...]
+        )
+        assert isinstance(path2, Path)
+        assert 3 == len(path2)
+        assert Vertex(11, 'person') == path2[0]
+        assert path2[0].properties == []
+        assert isinstance(path2[1], Edge)
+        assert path2[1].properties == []
+        assert path2[2] == "hello"
+
+        # mixed path: first vertex with properties, second vertex without
+        path3 = self.graphson_reader.read_object(
+            
"""{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["a"]},{"@type":"g:Set","@value":["b"]}]},"objects":{"@type":"g:List","@value":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}]}}}"""
+        )
+        assert isinstance(path3, Path)
+        assert 2 == len(path3)
+        assert Vertex(1) == path3[0]
+        assert any(vp.label == 'name' and vp.value == 'marko' for vp in 
path3[0].properties)
+        assert Vertex(2) == path3[1]
+        assert path3[1].properties == []
 
     def test_custom_mapping(self):
 
diff --git a/gremlin-python/src/main/python/tests/structure/test_graph.py 
b/gremlin-python/src/main/python/tests/structure/test_graph.py
index 0937e3998b..8b7fe38300 100644
--- a/gremlin-python/src/main/python/tests/structure/test_graph.py
+++ b/gremlin-python/src/main/python/tests/structure/test_graph.py
@@ -34,6 +34,8 @@ class TestGraph(object):
         assert "vertex" == vertex.label
         assert "person" == Vertex(1, "person").label
         assert vertex == Vertex(1)
+        # properties default to empty list when not provided
+        assert vertex.properties == []
         #
         edge = Edge(2, Vertex(1), "said", Vertex("hello", "phrase"))
         assert "e[2][1-said->hello]" == str(edge)
@@ -42,6 +44,8 @@ class TestGraph(object):
         assert "said" == edge.label
         assert "phrase" == edge.inV.label
         assert edge.inV != edge.outV
+        # properties default to empty list when not provided
+        assert edge.properties == []
         #
         vertex_property = VertexProperty(long(24), "name", "marko", Vertex(1))
         assert "vp[name->marko]" == str(vertex_property)
@@ -52,6 +56,8 @@ class TestGraph(object):
         assert Vertex(1) == vertex_property.vertex
         assert isinstance(vertex_property.id, long)
         assert vertex_property == VertexProperty(long(24), "name", "marko", 
Vertex(1))
+        # meta-properties default to empty list when not provided
+        assert vertex_property.properties == []
         #
         property = Property("age", 29, Vertex(1))
         assert "p[age->29]" == str(property)
@@ -61,6 +67,23 @@ class TestGraph(object):
         assert isinstance(property.value, int)
         assert property == Property("age", 29, Vertex(1))
         #
+        # Now create elements with properties explicitly set
+        v2 = Vertex(10, "person", [VertexProperty(100, "name", "marko", 
Vertex(10))])
+        assert len(v2.properties) == 1
+        assert isinstance(v2.properties[0], VertexProperty)
+        assert v2.properties[0].label == "name"
+        assert v2.properties[0].value == "marko"
+        e2 = Edge(20, Vertex(10), "knows", Vertex(11), [Property("weight", 
0.5, None)])
+        assert len(e2.properties) == 1
+        assert isinstance(e2.properties[0], Property)
+        assert e2.properties[0].key == "weight"
+        assert e2.properties[0].value == 0.5
+        vp2 = VertexProperty(30, "name", "marko", Vertex(10), 
[Property("since", 2006, None)])
+        assert len(vp2.properties) == 1
+        assert isinstance(vp2.properties[0], Property)
+        assert vp2.properties[0].key == "since"
+        assert vp2.properties[0].value == 2006
+        #
         for i in [vertex, edge, vertex_property, property]:
             for j in [vertex, edge, vertex_property, property]:
                 if type(i) != type(j):

Reply via email to