You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by fl...@apache.org on 2020/12/04 11:11:34 UTC

[tinkerpop] 01/01: TINKERPOP-2472 Decouple the driver from the IO format

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

florianhockmann pushed a commit to branch TINKERPOP-2472
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 33be8617d025711d902594821f1ed47dedf84b18
Author: Florian Hockmann <fh...@florian-hockmann.de>
AuthorDate: Fri Dec 4 11:56:14 2020 +0100

    TINKERPOP-2472 Decouple the driver from the IO format
    
    It is now easier to use the driver with a serialization format that is
    not GraphSON or even JSON based. This should allow us to support
    GraphBinary.
    
    NOTE: The driver now always deserializes a result completely, e.g.,
    skipping serialization like it was implemented in TINKERPOP-2067 is no
    longer supported. However, users who need this functionality can simply
    implement their own IMessageSerializer with this functionality.
---
 CHANGELOG.asciidoc                                 |   1 +
 .../src/Gremlin.Net/Driver/Connection.cs           |  29 ++---
 .../src/Gremlin.Net/Driver/ConnectionFactory.cs    |  18 +--
 .../src/Gremlin.Net/Driver/GremlinClient.cs        | 122 +++++++++++++++++----
 .../src/Gremlin.Net/Driver/IMessageSerializer.cs   |  48 ++++++++
 .../Gremlin.Net/Driver/JsonMessageSerializer.cs    |  60 ----------
 .../Gremlin.Net/Driver/Messages/ResponseMessage.cs |  17 ++-
 .../Gremlin.Net/Driver/Messages/ResponseResult.cs  |  18 ++-
 .../Gremlin.Net/Driver/Messages/ResponseStatus.cs  |  18 ++-
 ...s => ResponseHandlerForSingleRequestMessage.cs} |  19 +---
 .../IO/GraphSON/GraphSON2MessageSerializer.cs      |  43 ++++++++
 .../IO/GraphSON/GraphSON3MessageSerializer.cs      |  43 ++++++++
 .../IO/GraphSON/GraphSONMessageSerializer.cs       | 100 +++++++++++++++++
 .../IO/SerializationTokens.cs}                     |  20 ++--
 .../Docs/Dev/Provider/IndexTests.cs                |   2 +-
 .../Docs/Reference/GremlinVariantsTests.cs         |   3 +-
 .../Driver/GremlinClientTests.cs                   |  26 -----
 ...rTests.cs => GraphSONMessageSerializerTests.cs} |  26 +++--
 .../Driver/GremlinClientTests.cs                   |  39 ++++++-
 .../IO/GraphSON/BytecodeGraphSONSerializerTests.cs |   3 +-
 20 files changed, 470 insertions(+), 185 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 1521246..595aa73 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Ensured better consistency of the use of `null` as arguments to mutation steps.
 * Allowed `property(T.label,Object)` to be used if no value was supplied to `addV(String)`.
 * Allowed additional arguments to `Client.submit()` in Javascript driver to enable setting of parameters like `scriptEvaluationTimeout`.
+* Gremlin.Net driver no longer supports skipping deserialization by default. User can however create their own `IMessageSerializer` if they need this functionality.
 * Supported deserialization of `dict` as a key in a `dict` for Python.
 * Added a `Graph.Feature` for `supportsNullPropertyValues`.
 * Modified `TokenTraversal` to support `Property` thus `by(key)` and `by(value)` can now apply to `Edge` and meta-properties.
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
index 9bc4658..0b55b6a 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Connection.cs
@@ -30,22 +30,19 @@ using System.Threading;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
 using Gremlin.Net.Process;
-using Gremlin.Net.Structure.IO.GraphSON;
 
 namespace Gremlin.Net.Driver
 {
     internal interface IResponseHandlerForSingleRequestMessage
     {
-        void HandleReceived(ResponseMessage received);
+        void HandleReceived(ResponseMessage<List<object>> received);
         void Finalize(Dictionary<string, object> statusAttributes);
         void HandleFailure(Exception objException);
     }
 
     internal class Connection : IConnection
     {
-        private readonly GraphSONReader _graphSONReader;
-        private readonly GraphSONWriter _graphSONWriter;
-        private readonly JsonMessageSerializer _messageSerializer;
+        private readonly IMessageSerializer _messageSerializer;
         private readonly Uri _uri;
         private readonly WebSocketConnection _webSocketConnection;
         private readonly string _username;
@@ -60,8 +57,7 @@ namespace Gremlin.Net.Driver
         private int _writeInProgress = 0;
         private const int Closed = 1;
 
-        public Connection(Uri uri, string username, string password, GraphSONReader graphSONReader,
-            GraphSONWriter graphSONWriter, string mimeType,
+        public Connection(Uri uri, string username, string password, IMessageSerializer messageSerializer,
             Action<ClientWebSocketOptions> webSocketConfiguration, string sessionId)
         {
             _uri = uri;
@@ -72,9 +68,7 @@ namespace Gremlin.Net.Driver
             {
                 _sessionEnabled = true;
             }
-            _graphSONReader = graphSONReader;
-            _graphSONWriter = graphSONWriter;
-            _messageSerializer = new JsonMessageSerializer(mimeType);
+            _messageSerializer = messageSerializer;
             _webSocketConnection = new WebSocketConnection(webSocketConfiguration);
         }
 
@@ -90,7 +84,7 @@ namespace Gremlin.Net.Driver
 
         public Task<ResultSet<T>> SubmitAsync<T>(RequestMessage requestMessage)
         {
-            var receiver = new ResponseHandlerForSingleRequestMessage<T>(_graphSONReader);
+            var receiver = new ResponseHandlerForSingleRequestMessage<T>();
             _callbackByRequestId.GetOrAdd(requestMessage.RequestId, receiver);
             _writeQueue.Enqueue(requestMessage);
             BeginSendingMessages();
@@ -111,7 +105,7 @@ namespace Gremlin.Net.Driver
                 try
                 {
                     var received = await _webSocketConnection.ReceiveMessageAsync().ConfigureAwait(false);
-                    Parse(received);
+                    HandleReceived(received);
                 }
                 catch (Exception e)
                 {
@@ -121,9 +115,9 @@ namespace Gremlin.Net.Driver
             }
         }
 
-        private void Parse(byte[] received)
+        private void HandleReceived(byte[] received)
         {
-            var receivedMsg = _messageSerializer.DeserializeMessage<ResponseMessage>(received);
+            var receivedMsg = _messageSerializer.DeserializeMessage(received);
             if (receivedMsg == null)
             {
                 ThrowMessageDeserializedNull();
@@ -131,7 +125,7 @@ namespace Gremlin.Net.Driver
 
             try
             {
-                TryParseResponseMessage(receivedMsg);
+                HandleReceivedMessage(receivedMsg);
             }
             catch (Exception e)
             {
@@ -145,7 +139,7 @@ namespace Gremlin.Net.Driver
         private static void ThrowMessageDeserializedNull() =>
             throw new InvalidOperationException("Received data deserialized into null object message. Cannot operate on it.");
 
-        private void TryParseResponseMessage(ResponseMessage receivedMsg)
+        private void HandleReceivedMessage(ResponseMessage<List<object>> receivedMsg)
         {
             var status = receivedMsg.Status;
             status.ThrowIfStatusIndicatesError();
@@ -246,8 +240,7 @@ namespace Gremlin.Net.Driver
             {
                 message = RebuildSessionMessage(message);
             }
-            var graphsonMsg = _graphSONWriter.WriteObject(message);
-            var serializedMsg = _messageSerializer.SerializeMessage(graphsonMsg);
+            var serializedMsg = _messageSerializer.SerializeMessage(message);
             await _webSocketConnection.SendMessageAsync(serializedMsg).ConfigureAwait(false);
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
index 9ef32a5..59807a1 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionFactory.cs
@@ -1,4 +1,4 @@
-#region License
+#region License
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,35 +23,29 @@
 
 using System;
 using System.Net.WebSockets;
-using Gremlin.Net.Structure.IO.GraphSON;
 
 namespace Gremlin.Net.Driver
 {
     internal class ConnectionFactory : IConnectionFactory
     {
-        private readonly GraphSONReader _graphSONReader;
-        private readonly GraphSONWriter _graphSONWriter;
         private readonly Action<ClientWebSocketOptions> _webSocketConfiguration;
         private readonly GremlinServer _gremlinServer;
-        private readonly string _mimeType;
         private readonly string _sessionId;
+        private IMessageSerializer _messageSerializer;
 
-        public ConnectionFactory(GremlinServer gremlinServer, GraphSONReader graphSONReader,
-            GraphSONWriter graphSONWriter, string mimeType,
+        public ConnectionFactory(GremlinServer gremlinServer, IMessageSerializer messageSerializer,
             Action<ClientWebSocketOptions> webSocketConfiguration, string sessionId)
         {
             _gremlinServer = gremlinServer;
-            _mimeType = mimeType;
+            _messageSerializer = messageSerializer;
             _sessionId = sessionId;
-            _graphSONReader = graphSONReader ?? throw new ArgumentNullException(nameof(graphSONReader));
-            _graphSONWriter = graphSONWriter ?? throw new ArgumentNullException(nameof(graphSONWriter));
             _webSocketConfiguration = webSocketConfiguration;
         }
 
         public IConnection CreateConnection()
         {
-            return new Connection(_gremlinServer.Uri, _gremlinServer.Username, _gremlinServer.Password, _graphSONReader,
-                                 _graphSONWriter, _mimeType, _webSocketConfiguration, _sessionId);
+            return new Connection(_gremlinServer.Uri, _gremlinServer.Username, _gremlinServer.Password,
+                _messageSerializer, _webSocketConfiguration, _sessionId);
         }
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
index bf637bf..745a3cc 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
@@ -22,10 +22,10 @@
 #endregion
 
 using System;
-using System.Collections.Generic;
 using System.Net.WebSockets;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
+using Gremlin.Net.Structure.IO;
 using Gremlin.Net.Structure.IO.GraphSON;
 
 namespace Gremlin.Net.Driver
@@ -35,18 +35,29 @@ namespace Gremlin.Net.Driver
     /// </summary>
     public class GremlinClient : IGremlinClient
     {
-        /// <summary>
-        /// Defines the default mime type to use.
-        /// </summary>
-        public const string DefaultMimeType = "application/vnd.gremlin-v3.0+json";
+        private readonly ConnectionPool _connectionPool;
 
         /// <summary>
-        /// The GraphSON2 mime type to use.
+        ///     Initializes a new instance of the <see cref="GremlinClient" /> class for the specified Gremlin Server.
         /// </summary>
-        public const string GraphSON2MimeType = "application/vnd.gremlin-v2.0+json";
+        /// <param name="gremlinServer">The <see cref="GremlinServer" /> the requests should be sent to.</param>
+        /// <param name="graphSONReader">A <see cref="GraphSONReader" /> instance to read received GraphSON data.</param>
+        /// <param name="graphSONWriter">a <see cref="GraphSONWriter" /> instance to write GraphSON data.</param>
+        /// <param name="connectionPoolSettings">The <see cref="ConnectionPoolSettings" /> for the connection pool.</param>
+        /// <param name="webSocketConfiguration">
+        ///     A delegate that will be invoked with the <see cref="ClientWebSocketOptions" />
+        ///     object used to configure WebSocket connections.
+        /// </param>
+        /// <param name="sessionId">The session Id if Gremlin Client in session mode, defaults to null as session-less Client.</param>
+        [Obsolete("This constructor is obsolete. Use the constructor that takes a IMessageSerializer instead.")]
+        public GremlinClient(GremlinServer gremlinServer, GraphSONReader graphSONReader, GraphSONWriter graphSONWriter,
+            ConnectionPoolSettings connectionPoolSettings = null,
+            Action<ClientWebSocketOptions> webSocketConfiguration = null, string sessionId = null)
+            : this(gremlinServer, graphSONReader, graphSONWriter, SerializationTokens.GraphSON3MimeType,
+                connectionPoolSettings, webSocketConfiguration, sessionId)
+        {
+        }
         
-        private readonly ConnectionPool _connectionPool;
-
         /// <summary>
         ///     Initializes a new instance of the <see cref="GremlinClient" /> class for the specified Gremlin Server.
         /// </summary>
@@ -54,24 +65,96 @@ namespace Gremlin.Net.Driver
         /// <param name="graphSONReader">A <see cref="GraphSONReader" /> instance to read received GraphSON data.</param>
         /// <param name="graphSONWriter">a <see cref="GraphSONWriter" /> instance to write GraphSON data.</param>
         /// <param name="mimeType">The GraphSON version mime type, defaults to latest supported by the server.</param>
-        /// <param name="connectionPoolSettings">The <see cref="ConnectionPoolSettings"/> for the connection pool.</param>
+        /// <param name="connectionPoolSettings">The <see cref="ConnectionPoolSettings" /> for the connection pool.</param>
+        /// <param name="webSocketConfiguration">
+        ///     A delegate that will be invoked with the <see cref="ClientWebSocketOptions" />
+        ///     object used to configure WebSocket connections.
+        /// </param>
+        /// <param name="sessionId">The session Id if Gremlin Client in session mode, defaults to null as session-less Client.</param>
+        [Obsolete("This constructor is obsolete. Use the constructor that takes a IMessageSerializer instead.")]
+        public GremlinClient(GremlinServer gremlinServer, GraphSONReader graphSONReader, GraphSONWriter graphSONWriter,
+            string mimeType, ConnectionPoolSettings connectionPoolSettings = null,
+            Action<ClientWebSocketOptions> webSocketConfiguration = null, string sessionId = null)
+        {
+            IMessageSerializer messageSerializer;
+            switch (mimeType)
+            {
+                case SerializationTokens.GraphSON3MimeType:
+                    VerifyGraphSONArgumentTypeForMimeType<GraphSON3Reader>(graphSONReader, nameof(graphSONReader),
+                        mimeType);
+                    VerifyGraphSONArgumentTypeForMimeType<GraphSON3Writer>(graphSONWriter, nameof(graphSONWriter),
+                        mimeType);
+                    messageSerializer = new GraphSON3MessageSerializer(
+                        (GraphSON3Reader) graphSONReader ?? new GraphSON3Reader(),
+                        (GraphSON3Writer) graphSONWriter ?? new GraphSON3Writer());
+                    break;
+                case SerializationTokens.GraphSON2MimeType:
+                    VerifyGraphSONArgumentTypeForMimeType<GraphSON2Reader>(graphSONReader, nameof(graphSONReader),
+                        mimeType);
+                    VerifyGraphSONArgumentTypeForMimeType<GraphSON2Writer>(graphSONWriter, nameof(graphSONWriter),
+                        mimeType);
+                    messageSerializer = new GraphSON2MessageSerializer(
+                        (GraphSON2Reader) graphSONReader ?? new GraphSON2Reader(),
+                        (GraphSON2Writer) graphSONWriter ?? new GraphSON2Writer());
+                    break;
+                default:
+                    throw new ArgumentException(nameof(mimeType), $"{mimeType} not supported");
+            }
+            
+            var connectionFactory =
+                new ConnectionFactory(gremlinServer, messageSerializer, webSocketConfiguration, sessionId);
+
+            // make sure one connection in pool as session mode
+            if (!string.IsNullOrEmpty(sessionId))
+            {
+                if (connectionPoolSettings != null)
+                {
+                    if (connectionPoolSettings.PoolSize != 1)
+                        throw new ArgumentOutOfRangeException(nameof(connectionPoolSettings), "PoolSize must be 1 in session mode!");
+                }
+                else
+                {
+                    connectionPoolSettings = new ConnectionPoolSettings {PoolSize = 1};
+                }
+            }
+            _connectionPool =
+                new ConnectionPool(connectionFactory, connectionPoolSettings ?? new ConnectionPoolSettings());
+        }
+
+        private static void VerifyGraphSONArgumentTypeForMimeType<T>(object argument, string argumentName,
+            string mimeType)
+        {
+            if (argument != null && !(argument is T))
+            {
+                throw new ArgumentException(
+                    $"{argumentName} is not a {typeof(T).Name} but the mime type is: {mimeType}", argumentName);
+            }
+        }
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GremlinClient" /> class for the specified Gremlin Server.
+        /// </summary>
+        /// <param name="gremlinServer">The <see cref="GremlinServer" /> the requests should be sent to.</param>
+        /// <param name="messageSerializer">
+        ///     A <see cref="IMessageSerializer" /> instance to serialize messages sent to and received
+        ///     from the server.
+        /// </param>
+        /// <param name="connectionPoolSettings">The <see cref="ConnectionPoolSettings" /> for the connection pool.</param>
         /// <param name="webSocketConfiguration">
         ///     A delegate that will be invoked with the <see cref="ClientWebSocketOptions" />
         ///     object used to configure WebSocket connections.
         /// </param>
         /// <param name="sessionId">The session Id if Gremlin Client in session mode, defaults to null as session-less Client.</param>
-        public GremlinClient(GremlinServer gremlinServer, GraphSONReader graphSONReader = null,
-            GraphSONWriter graphSONWriter = null, string mimeType = null,
+        public GremlinClient(GremlinServer gremlinServer, IMessageSerializer messageSerializer = null,
             ConnectionPoolSettings connectionPoolSettings = null,
             Action<ClientWebSocketOptions> webSocketConfiguration = null, string sessionId = null)
         {
-            var reader = graphSONReader ?? new GraphSON3Reader();
-            var writer = graphSONWriter ?? new GraphSON3Writer();
-            var connectionFactory = new ConnectionFactory(gremlinServer, reader, writer, mimeType ?? DefaultMimeType,
-                webSocketConfiguration, sessionId);
+            messageSerializer = messageSerializer ?? new GraphSON3MessageSerializer();
+            var connectionFactory =
+                new ConnectionFactory(gremlinServer, messageSerializer, webSocketConfiguration, sessionId);
 
             // make sure one connection in pool as session mode
-            if (!String.IsNullOrEmpty(sessionId))
+            if (!string.IsNullOrEmpty(sessionId))
             {
                 if (connectionPoolSettings != null)
                 {
@@ -80,12 +163,11 @@ namespace Gremlin.Net.Driver
                 }
                 else
                 {
-                    connectionPoolSettings = new ConnectionPoolSettings();
-                    connectionPoolSettings.PoolSize = 1;
+                    connectionPoolSettings = new ConnectionPoolSettings {PoolSize = 1};
                 }
             }
             _connectionPool =
-                new ConnectionPool(connectionFactory, connectionPoolSettings ?? new ConnectionPoolSettings());            
+                new ConnectionPool(connectionFactory, connectionPoolSettings ?? new ConnectionPoolSettings());
         }
 
         /// <summary>
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/IMessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/IMessageSerializer.cs
new file mode 100644
index 0000000..bdd9095
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/IMessageSerializer.cs
@@ -0,0 +1,48 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System.Collections.Generic;
+using Gremlin.Net.Driver.Messages;
+
+namespace Gremlin.Net.Driver
+{
+    /// <summary>
+    ///     Serializes data to and from Gremlin Server.
+    /// </summary>
+    public interface IMessageSerializer
+    {
+        /// <summary>
+        ///     Serializes a <see cref="RequestMessage"/>.
+        /// </summary>
+        /// <param name="requestMessage">The <see cref="RequestMessage"/> to serialize.</param>
+        /// <returns>The serialized message.</returns>
+        byte[] SerializeMessage(RequestMessage requestMessage);
+        
+        /// <summary>
+        ///     Deserializes a <see cref="ResponseMessage{T}"/> from a byte array.
+        /// </summary>
+        /// <param name="message">The serialized message to deserialize.</param>
+        /// <returns>The deserialized <see cref="ResponseMessage{T}"/>.</returns>
+        ResponseMessage<List<object>> DeserializeMessage(byte[] message);
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/JsonMessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/JsonMessageSerializer.cs
deleted file mode 100644
index de4c610..0000000
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/JsonMessageSerializer.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-#region License
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#endregion
-
-using System;
-using System.Text;
-using System.Text.Json;
-
-namespace Gremlin.Net.Driver
-{
-    internal class JsonMessageSerializer
-    {
-        private readonly string _mimeType;
-        private static readonly JsonSerializerOptions JsonDeserializingOptions = new JsonSerializerOptions
-            {PropertyNamingPolicy = JsonNamingPolicy.CamelCase};
-
-        public JsonMessageSerializer(string mimeType)
-        {
-            _mimeType = mimeType;
-        }
-
-        public byte[] SerializeMessage(string msg)
-        {
-            return Encoding.UTF8.GetBytes(MessageWithHeader(msg));
-        }
-
-        private string MessageWithHeader(string messageContent)
-        {
-            return $"{(char) _mimeType.Length}{_mimeType}{messageContent}";
-        }
-
-        public TMessage DeserializeMessage<TMessage>(byte[] message)
-        {
-            if (message == null) throw new ArgumentNullException(nameof(message));
-            if (message.Length == 0) return default;
-            var reader = new Utf8JsonReader(message);
-            var deserialized = JsonSerializer.Deserialize<TMessage>(ref reader, JsonDeserializingOptions);
-            return deserialized;
-        }
-    }
-}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseMessage.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseMessage.cs
index 791433f..b8c0673 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseMessage.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseMessage.cs
@@ -25,12 +25,25 @@ using System;
 
 namespace Gremlin.Net.Driver.Messages
 {
-    internal class ResponseMessage
+    /// <summary>
+    ///     The message returned from the server.
+    /// </summary>
+    /// <typeparam name="T">The type of the data returned.</typeparam>
+    public class ResponseMessage<T>
     {
+        /// <summary>
+        ///     Gets or sets the identifier of the <see cref="RequestMessage"/> that generated this <see cref="ResponseMessage{T}"/>.
+        /// </summary>
         public Guid RequestId { get; set; }
 
+        /// <summary>
+        ///     Gets or sets status information about this <see cref="ResponseMessage{T}"/>.
+        /// </summary>
         public ResponseStatus Status { get; set; }
 
-        public ResponseResult Result { get; set; }
+        /// <summary>
+        ///     Gets or sets the result with its data and optional meta information.
+        /// </summary>
+        public ResponseResult<T> Result { get; set; }
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs
index b26f415..abc2907 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs
@@ -22,14 +22,24 @@
 #endregion
 
 using System.Collections.Generic;
-using System.Text.Json;
 
 namespace Gremlin.Net.Driver.Messages
 {
-    internal class ResponseResult
+    /// <summary>
+    ///     Represents the result as a response to a <see cref="RequestMessage"/> sent as part of a
+    ///     <see cref="ResponseMessage{T}"/> by the server.
+    /// </summary>
+    /// <typeparam name="T">The type of the <see cref="Data"/>.</typeparam>
+    public class ResponseResult<T>
     {
-        public JsonElement Data { get; set; }
-        
+        /// <summary>
+        ///     Gets or sets the data of this result.
+        /// </summary>
+        public T Data { get; set; }
+
+        /// <summary>
+        ///     Gets or sets meta data of this result.
+        /// </summary>
         public Dictionary<string, object> Meta { get; set; }
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatus.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatus.cs
index 32d8b43..63530a3 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatus.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseStatus.cs
@@ -26,12 +26,24 @@ using Gremlin.Net.Driver.Exceptions;
 
 namespace Gremlin.Net.Driver.Messages
 {
-    internal class ResponseStatus
+    /// <summary>
+    ///     Represents status information of a <see cref="ResponseMessage{T}"/>.
+    /// </summary>
+    public class ResponseStatus
     {
+        /// <summary>
+        ///     Gets or sets the <see cref="ResponseStatusCode"/>.
+        /// </summary>
         public ResponseStatusCode Code { get; set; }
-        
+
+        /// <summary>
+        ///     Gets or sets the attributes <see cref="Dictionary{TKey,TValue}"/> with protocol-level information.
+        /// </summary>
         public Dictionary<string, object> Attributes { get; set; }
-        
+
+        /// <summary>
+        ///     Gets or sets the message which is just a human-readable string usually associated with errors.
+        /// </summary>
         public string Message { get; set; }
     }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/SingleMessageResultReceiver.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/ResponseHandlerForSingleRequestMessage.cs
similarity index 75%
rename from gremlin-dotnet/src/Gremlin.Net/Driver/SingleMessageResultReceiver.cs
rename to gremlin-dotnet/src/Gremlin.Net/Driver/ResponseHandlerForSingleRequestMessage.cs
index 13fbaf4..fafa7d9 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/SingleMessageResultReceiver.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/ResponseHandlerForSingleRequestMessage.cs
@@ -23,10 +23,8 @@
 
 using System;
 using System.Collections.Generic;
-using System.Text.Json;
 using System.Threading.Tasks;
 using Gremlin.Net.Driver.Messages;
-using Gremlin.Net.Structure.IO.GraphSON;
 
 namespace Gremlin.Net.Driver
 {
@@ -36,23 +34,14 @@ namespace Gremlin.Net.Driver
 
         private readonly TaskCompletionSource<ResultSet<T>> _tcs =
             new TaskCompletionSource<ResultSet<T>>(TaskCreationOptions.RunContinuationsAsynchronously);
-
-        private readonly GraphSONReader _graphSONReader;
+        
         private readonly List<T> _result = new List<T>();
 
-        public ResponseHandlerForSingleRequestMessage(GraphSONReader graphSonReader)
-        {
-            _graphSONReader = graphSonReader;
-        }
-
-        public void HandleReceived(ResponseMessage received)
+        public void HandleReceived(ResponseMessage<List<object>> received)
         {
-            var receivedData = typeof(T) == typeof(JsonElement)
-                ? new[] {received.Result.Data}
-                : _graphSONReader.ToObject(received.Result.Data);
-            foreach (var d in receivedData)
+            foreach (var d in received.Result.Data)
             {
-                _result.Add(d);
+                _result.Add((T) d);
             }
         }
 
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON2MessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON2MessageSerializer.cs
new file mode 100644
index 0000000..c0c36b1
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON2MessageSerializer.cs
@@ -0,0 +1,43 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+namespace Gremlin.Net.Structure.IO.GraphSON
+{
+    /// <summary>
+    ///     Serializes data to and from Gremlin Server in GraphSON2 format.
+    /// </summary>
+    public class GraphSON2MessageSerializer : GraphSONMessageSerializer
+    {
+        private const string MimeType = SerializationTokens.GraphSON2MimeType;
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GraphSON2MessageSerializer" /> class with custom serializers.
+        /// </summary>
+        /// <param name="graphSONReader">The <see cref="GraphSON2Reader"/> used to deserialize from GraphSON.</param>
+        /// <param name="graphSONWriter">The <see cref="GraphSON2Writer"/> used to serialize to GraphSON.</param>
+        public GraphSON2MessageSerializer(GraphSON2Reader graphSONReader = null, GraphSON2Writer graphSONWriter = null)
+            : base(MimeType, graphSONReader ?? new GraphSON2Reader(), graphSONWriter ?? new GraphSON2Writer())
+        {
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON3MessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON3MessageSerializer.cs
new file mode 100644
index 0000000..abb6a75
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSON3MessageSerializer.cs
@@ -0,0 +1,43 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+namespace Gremlin.Net.Structure.IO.GraphSON
+{
+    /// <summary>
+    ///     Serializes data to and from Gremlin Server in GraphSON3 format.
+    /// </summary>
+    public class GraphSON3MessageSerializer : GraphSONMessageSerializer
+    {
+        private const string MimeType = SerializationTokens.GraphSON3MimeType;
+        
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GraphSON3MessageSerializer" /> class with custom serializers.
+        /// </summary>
+        /// <param name="graphSONReader">The <see cref="GraphSON3Reader"/> used to deserialize from GraphSON.</param>
+        /// <param name="graphSONWriter">The <see cref="GraphSON3Writer"/> used to serialize to GraphSON.</param>
+        public GraphSON3MessageSerializer(GraphSON3Reader graphSONReader = null, GraphSON3Writer graphSONWriter = null)
+            : base(MimeType, graphSONReader ?? new GraphSON3Reader(), graphSONWriter ?? new GraphSON3Writer())
+        {
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONMessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONMessageSerializer.cs
new file mode 100644
index 0000000..8c73319
--- /dev/null
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONMessageSerializer.cs
@@ -0,0 +1,100 @@
+#region License
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using Gremlin.Net.Driver;
+using Gremlin.Net.Driver.Messages;
+
+namespace Gremlin.Net.Structure.IO.GraphSON
+{
+    /// <summary>
+    ///     Serializes data to and from Gremlin Server in GraphSON format.
+    /// </summary>
+    public abstract class GraphSONMessageSerializer : IMessageSerializer
+    {
+        private static readonly JsonSerializerOptions JsonDeserializingOptions = new JsonSerializerOptions
+            {PropertyNamingPolicy = JsonNamingPolicy.CamelCase};
+        private readonly string _mimeType;
+        private readonly GraphSONReader _graphSONReader;
+        private readonly GraphSONWriter _graphSONWriter;
+
+        /// <summary>
+        ///     Initializes a new instance of the <see cref="GraphSONMessageSerializer" /> class.
+        /// </summary>
+        /// <param name="mimeType">The MIME type supported by this serializer.</param>
+        /// <param name="graphSONReader">The <see cref="GraphSONReader"/> used to deserialize from GraphSON.</param>
+        /// <param name="graphSonWriter">The <see cref="GraphSONWriter"/> used to serialize to GraphSON.</param>
+        protected GraphSONMessageSerializer(string mimeType, GraphSONReader graphSONReader,
+            GraphSONWriter graphSonWriter)
+        {
+            _mimeType = mimeType;
+            _graphSONReader = graphSONReader;
+            _graphSONWriter = graphSonWriter;
+        }
+
+        /// <inheritdoc />
+        public byte[] SerializeMessage(RequestMessage requestMessage)
+        {
+            var graphSONMessage = _graphSONWriter.WriteObject(requestMessage);
+            return Encoding.UTF8.GetBytes(MessageWithHeader(graphSONMessage));
+        }
+
+        private string MessageWithHeader(string messageContent)
+        {
+            return $"{(char) _mimeType.Length}{_mimeType}{messageContent}";
+        }
+
+        /// <inheritdoc />
+        public ResponseMessage<List<object>> DeserializeMessage(byte[] message)
+        {
+            if (message == null) throw new ArgumentNullException(nameof(message));
+            if (message.Length == 0) return default;
+            
+            var reader = new Utf8JsonReader(message);
+            var responseMessage =
+                JsonSerializer.Deserialize<ResponseMessage<JsonElement>>(ref reader, JsonDeserializingOptions);
+            if (responseMessage == null) return null;
+            
+            var data = _graphSONReader.ToObject(responseMessage.Result.Data);
+            return CopyMessageWithNewData(responseMessage, data);
+        }
+
+        private static ResponseMessage<List<object>> CopyMessageWithNewData(ResponseMessage<JsonElement> origMsg,
+            dynamic data)
+        {
+            return new ResponseMessage<List<object>>
+            {
+                RequestId = origMsg.RequestId,
+                Status = origMsg.Status,
+                Result = new ResponseResult<List<object>>
+                {
+                    Data = data == null ? null : new List<object>(data),
+                    Meta = origMsg.Result.Meta
+                }
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/SerializationTokens.cs
similarity index 62%
copy from gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs
copy to gremlin-dotnet/src/Gremlin.Net/Structure/IO/SerializationTokens.cs
index b26f415..95cb124 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/Messages/ResponseResult.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/SerializationTokens.cs
@@ -21,15 +21,21 @@
 
 #endregion
 
-using System.Collections.Generic;
-using System.Text.Json;
-
-namespace Gremlin.Net.Driver.Messages
+namespace Gremlin.Net.Structure.IO
 {
-    internal class ResponseResult
+    /// <summary>
+    ///     String constants used for serialization.
+    /// </summary>
+    public class SerializationTokens
     {
-        public JsonElement Data { get; set; }
+        /// <summary>
+        ///     The MIME type for GraphSON 2.
+        /// </summary>
+        public const string GraphSON2MimeType = "application/vnd.gremlin-v2.0+json";
         
-        public Dictionary<string, object> Meta { get; set; }
+        /// <summary>
+        ///     The MIME type for GraphSON 3.
+        /// </summary>
+        public const string GraphSON3MimeType = "application/vnd.gremlin-v3.0+json";
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Dev/Provider/IndexTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Dev/Provider/IndexTests.cs
index 906a117..24971b3 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Dev/Provider/IndexTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Dev/Provider/IndexTests.cs
@@ -84,7 +84,7 @@ var graphsonReader = new GraphSON3Reader(
 var graphsonWriter = new GraphSON3Writer(
     new Dictionary<Type, IGraphSONSerializer> {{typeof(MyType), new MyClassWriter()}});
 
-var gremlinClient = new GremlinClient(new GremlinServer("localhost", 8182), graphsonReader, graphsonWriter);
+var gremlinClient = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2MessageSerializer());
 // end::supportingGremlinNetIO[]
         }
     }
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
index 61264e0..234e6a7 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs
@@ -65,8 +65,7 @@ var g = Traversal().WithRemote(remoteConnection);
         public void SerializationTest()
         {
 // tag::serialization[]
-var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2Reader(),
-    new GraphSON2Writer(), GremlinClient.GraphSON2MimeType);
+var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2MessageSerializer());
 // end::serialization[]
         }
         
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs
index 377040c..d0e2c1c 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs
@@ -70,32 +70,6 @@ namespace Gremlin.Net.IntegrationTest.Driver
         }
 
         [Fact]
-        public async Task ShouldReturnResultWithoutDeserializingItForJsonElementType()
-        {
-            var gremlinServer = new GremlinServer(TestHost, TestPort);
-            using var gremlinClient = new GremlinClient(gremlinServer);
-            const string gremlinScript = "'someString'";
-                
-            var response = await gremlinClient.SubmitWithSingleResultAsync<JsonElement>(gremlinScript);
-
-            //Expected:
-            /* {
-                  "@type": "g:List",
-                  "@value": [
-                    "someString"
-                  ]
-                }*/
-
-            Assert.IsType<JsonElement>(response);
-            Assert.Equal("g:List", response.GetProperty("@type").GetString());
-
-            var valueProperty = response.GetProperty("@value");
-            Assert.NotNull(valueProperty);
-            Assert.Equal(1, valueProperty.GetArrayLength());
-            Assert.Equal("someString", (valueProperty[0].GetString()));
-        }
-
-        [Fact]
         public async Task ShouldHandleResponseWithoutContent()
         {
             var gremlinServer = new GremlinServer(TestHost, TestPort);
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/JsonMessageSerializerTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GraphSONMessageSerializerTests.cs
similarity index 67%
rename from gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/JsonMessageSerializerTests.cs
rename to gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GraphSONMessageSerializerTests.cs
index 79c1731..af8d9bf 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/JsonMessageSerializerTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GraphSONMessageSerializerTests.cs
@@ -21,48 +21,52 @@
 
 #endregion
 
-using Gremlin.Net.Driver;
-using Gremlin.Net.Driver.Messages;
 using System;
 using System.Text;
+using Gremlin.Net.Structure.IO.GraphSON;
 using Xunit;
 
 namespace Gremlin.Net.UnitTest.Driver
 {
-    public class JsonMessageSerializerTests
+    public class GraphSONMessageSerializerTests
     {
         [Fact]
         public void DeserializingNullThrows()
         {
-            var sut = new JsonMessageSerializer(GremlinClient.DefaultMimeType);
+            var sut = CreateMessageSerializer();
 
-            Assert.Throws<ArgumentNullException>(()=> sut.DeserializeMessage<ResponseMessage>(null));
+            Assert.Throws<ArgumentNullException>(()=> sut.DeserializeMessage(null));
         }
 
         [Fact]
         public void EmptyArrayDeserializedIntoNull()
         {
-            var sut = new JsonMessageSerializer(GremlinClient.DefaultMimeType);
+            var sut = CreateMessageSerializer();
 
-            Assert.Null(sut.DeserializeMessage<ResponseMessage>(new byte[0]));            
+            Assert.Null(sut.DeserializeMessage(new byte[0]));            
         }
 
         [Fact]
         public void EmptyStringDeserializedIntoNull()
         {
-            var sut = new JsonMessageSerializer(GremlinClient.DefaultMimeType);
+            var sut = CreateMessageSerializer();
             var ofEmpty = Encoding.UTF8.GetBytes("");
 
-            Assert.Null(sut.DeserializeMessage<ResponseMessage>(ofEmpty));
+            Assert.Null(sut.DeserializeMessage(ofEmpty));
         }
 
         [Fact]
         public void JsonNullDeserializedIntoNull()
         {
-            var sut = new JsonMessageSerializer(GremlinClient.DefaultMimeType);
+            var sut = CreateMessageSerializer();
             var ofNull = Encoding.UTF8.GetBytes("null");
 
-            Assert.Null(sut.DeserializeMessage<ResponseMessage>(ofNull));
+            Assert.Null(sut.DeserializeMessage(ofNull));
+        }
+
+        private static GraphSONMessageSerializer CreateMessageSerializer()
+        {
+            return new GraphSON3MessageSerializer(new GraphSON3Reader(), new GraphSON3Writer());
         }
     }
 }
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GremlinClientTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GremlinClientTests.cs
index 086dc90..5f1e4c5 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GremlinClientTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Driver/GremlinClientTests.cs
@@ -1,4 +1,4 @@
-#region License
+#region License
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -23,6 +23,8 @@
 
 using System;
 using Gremlin.Net.Driver;
+using Gremlin.Net.Structure.IO;
+using Gremlin.Net.Structure.IO.GraphSON;
 using Xunit;
 
 namespace Gremlin.Net.UnitTest.Driver
@@ -39,7 +41,40 @@ namespace Gremlin.Net.UnitTest.Driver
             var poolSettings = new ConnectionPoolSettings {PoolSize = 2};
 
             var gremlinServer = new GremlinServer(host, port);
-            Assert.Throws<ArgumentOutOfRangeException>(() => new GremlinClient(gremlinServer, connectionPoolSettings: poolSettings, sessionId: sessionId));
+            Assert.Throws<ArgumentOutOfRangeException>(() =>
+                new GremlinClient(gremlinServer, connectionPoolSettings: poolSettings, sessionId: sessionId));
         }
+
+#pragma warning disable 612,618
+        [Fact]
+        public void ShouldThrowForInvalidGraphSONReaderWriterCombination()
+        {
+            Assert.Throws<ArgumentException>(() =>
+                new GremlinClient(new GremlinServer(), new GraphSON2Reader(), new GraphSON3Writer()));
+        }
+        
+        [Fact]
+        public void ShouldThrowForInvalidGraphSONReaderForGivenMimeType()
+        {
+            Assert.Throws<ArgumentException>(() =>
+                new GremlinClient(new GremlinServer(), new GraphSON3Reader(), new GraphSON2Writer(),
+                    SerializationTokens.GraphSON2MimeType));
+        }
+        
+        [Fact]
+        public void ShouldThrowForInvalidGraphSONWriterForGivenMimeType()
+        {
+            Assert.Throws<ArgumentException>(() =>
+                new GremlinClient(new GremlinServer(), new GraphSON2Reader(), new GraphSON3Writer(),
+                    SerializationTokens.GraphSON2MimeType));
+        }
+
+        [Fact]
+        public void ShouldThrowForUnsupportedMimeType()
+        {
+            Assert.Throws<ArgumentException>(() =>
+                new GremlinClient(new GremlinServer(), new GraphSON3Reader(), new GraphSON3Writer(), "unsupported"));
+        }
+#pragma warning restore 612,618
     }
 }
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
index 29a434f..ad78334 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/BytecodeGraphSONSerializerTests.cs
@@ -1,4 +1,4 @@
-#region License
+#region License
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -22,7 +22,6 @@
 #endregion
 
 using System.Collections.Generic;
-using System.Numerics;
 using Gremlin.Net.Process.Traversal;
 using Gremlin.Net.Structure.IO.GraphSON;
 using Xunit;