Skip to content

Commit 0ddce90

Browse files
Http and Bolt clients serialize input parameters differently (#423)
* * Introduce property `SerializeNullValues` in `ExecutionConfiguration` class * Pass new `serializeNullValues` parameter to constructors * Modify `Neo4jDriverExtensions` to either preserve or skip null values of Objects and Dictionaries depending on congifuration * Introduce new tests to verify new logic * Change constructor parameter from `bool? serializeNullValues = null` to `bool serializeNullValues = false`
1 parent 2cfc88e commit 0ddce90

File tree

5 files changed

+106
-18
lines changed

5 files changed

+106
-18
lines changed

Neo4jClient.Tests/BoltGraphClientTests/BoltGraphClientTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,79 @@ public async Task SerializesGuidsProperlyWhenAutoGeneratingParams()
219219
var query = cfq.Query;
220220
query.ToNeo4jDriverParameters(bgc).IsEqualTo(expectedParameters).Should().BeTrue();
221221
}
222+
223+
[Fact]
224+
// Issue from https://github.com/DotNet4Neo4j/Neo4jClient/issues/419
225+
public async Task SerializesObjectProperlyWithoutNullPropertiesDefaultConfiguration()
226+
{
227+
var expectedParameters = new Dictionary<string, object>
228+
{
229+
{"param", new Dictionary<string, object> {{"Param1", 1}}}
230+
};
231+
232+
await SerializesObjectProperlyWitDifferentStrategies(
233+
expectedParameters,
234+
client => {}
235+
);
236+
}
237+
238+
[Fact]
239+
// Issue from https://github.com/DotNet4Neo4j/Neo4jClient/issues/419
240+
public async Task SerializesObjectProperlyWithNullProperties()
241+
{
242+
var expectedParameters = new Dictionary<string, object>
243+
{
244+
{"param", new Dictionary<string, object> {{"Param1", 1}, {"Param2", null}}}
245+
};
246+
247+
await SerializesObjectProperlyWitDifferentStrategies(
248+
expectedParameters,
249+
client => client.ExecutionConfiguration.SerializeNullValues = true
250+
);
251+
}
252+
253+
[Fact]
254+
// Issue from https://github.com/DotNet4Neo4j/Neo4jClient/issues/419
255+
public async Task SerializesObjectProperlyWithoutNullProperties()
256+
{
257+
var expectedParameters = new Dictionary<string, object>
258+
{
259+
{ "param", new Dictionary<string, object> { { "Param1", 1 } } }
260+
};
261+
262+
await SerializesObjectProperlyWitDifferentStrategies(
263+
expectedParameters,
264+
client => client.ExecutionConfiguration.SerializeNullValues = false
265+
);
266+
}
267+
268+
private static async Task SerializesObjectProperlyWitDifferentStrategies(IDictionary<string, object> expectedParameters, Action<BoltGraphClient> setupClient)
269+
{
270+
var mockSession = new Mock<IAsyncSession>();
271+
mockSession.Setup(s => s.RunAsync("CALL dbms.components()"))
272+
.Returns(Task.FromResult<IResultCursor>(new ServerInfo()));
273+
274+
var mockDriver = new Mock<IDriver>();
275+
mockDriver.Setup(d => d.AsyncSession(It.IsAny<Action<SessionConfigBuilder>>())).Returns(mockSession.Object);
276+
277+
var bgc = new BoltGraphClient(mockDriver.Object);
278+
await bgc.ConnectAsync();
279+
280+
setupClient(bgc);
281+
282+
var obj = new
283+
{
284+
Param1 = 1,
285+
Param2 = (object)null
286+
};
287+
288+
var cfq = bgc.Cypher.Create("MATCH (node:FakeNode) SET node += $param").WithParam("param", obj);
289+
290+
var query = cfq.Query;
291+
var t = query.ToNeo4jDriverParameters(bgc);
292+
t.IsEqualTo(expectedParameters).Should().BeTrue();
293+
}
294+
222295
//
223296
// [Fact]
224297
// public void RootNode_ThrowsInvalidOperationException()

Neo4jClient.Tests/TestUtilities.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ public static bool IsEqualTo<TKey, TValue>(this IDictionary<TKey, TValue> d1, ID
2121

2222
var v1 = d1[d1Key];
2323
var v2 = d2[d1Key];
24-
if (v1.GetType() == typeof(Dictionary<TKey, TValue>))
24+
if (v1?.GetType() == typeof(Dictionary<TKey, TValue>))
2525
return IsEqualTo((IDictionary<TKey, TValue>)v1, (IDictionary<TKey, TValue>)v2);
2626

27-
if (!d1[d1Key].Equals(d2[d1Key]))
27+
if(!Equals(d1[d1Key], d2[d1Key]))
2828
return false;
2929
}
3030
return true;

Neo4jClient/BoltGraphClient.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public class BoltGraphClient : IBoltGraphClient, IRawGraphClient, ITransactional
7070
/// <param name="username">The username to connect to Neo4j with.</param>
7171
/// <param name="password">The password to connect to Neo4j with.</param>
7272
/// <param name="realm">The realm to connect to Neo4j with.</param>
73-
public BoltGraphClient(Uri uri, IEnumerable<Uri> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null)
73+
public BoltGraphClient(Uri uri, IEnumerable<Uri> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null, bool serializeNullValues = false)
7474
{
7575
var localUris = uris?.ToList();
7676
if (localUris != null && localUris.Any())
@@ -104,30 +104,31 @@ public BoltGraphClient(Uri uri, IEnumerable<Uri> uris, string username = null, s
104104
JsonConverters = JsonConverters,
105105
Username = username,
106106
Password = password,
107-
Realm = realm
107+
Realm = realm,
108+
SerializeNullValues = serializeNullValues
108109
};
109110

110111
transactionManager = new BoltTransactionManager(this);
111112
}
112113

113-
public BoltGraphClient(Uri uri, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null)
114-
: this(uri, null, username, password, realm, encryptionLevel)
114+
public BoltGraphClient(Uri uri, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null, bool serializeNullValues = false)
115+
: this(uri, null, username, password, realm, encryptionLevel, serializeNullValues)
115116
{ }
116117

117-
public BoltGraphClient(IEnumerable<string> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null)
118-
: this(new Uri("neo4j://virtual.neo4j.uri"), uris?.Select(UriCreator.From).ToList(), username, password, realm, encryptionLevel)
118+
public BoltGraphClient(IEnumerable<string> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null, bool serializeNullValues = false)
119+
: this(new Uri("neo4j://virtual.neo4j.uri"), uris?.Select(UriCreator.From).ToList(), username, password, realm, encryptionLevel, serializeNullValues)
119120
{ }
120121

121-
public BoltGraphClient(string uri, IEnumerable<string> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null)
122-
: this(new Uri(uri), uris?.Select(UriCreator.From).ToList(), username, password, realm, encryptionLevel)
122+
public BoltGraphClient(string uri, IEnumerable<string> uris, string username = null, string password = null, string realm = null, EncryptionLevel? encryptionLevel = null, bool serializeNullValues = false)
123+
: this(new Uri(uri), uris?.Select(UriCreator.From).ToList(), username, password, realm, encryptionLevel, serializeNullValues)
123124
{}
124125

125-
public BoltGraphClient(string uri, string username = null, string password= null, string realm = null, EncryptionLevel? encryptionLevel = null)
126-
: this(new Uri(uri), username, password, realm, encryptionLevel)
126+
public BoltGraphClient(string uri, string username = null, string password= null, string realm = null, EncryptionLevel? encryptionLevel = null, bool serializeNullValues = false)
127+
: this(new Uri(uri), username, password, realm, encryptionLevel, serializeNullValues)
127128
{ }
128129

129130
public BoltGraphClient(IDriver driver)
130-
: this(new Uri("neo4j://Neo4j-Driver-Does-Not-Supply-This/"), null, null, null, null)
131+
: this(new Uri("neo4j://Neo4j-Driver-Does-Not-Supply-This/"), null, null, null, null, false)
131132
{
132133
Driver = driver;
133134
}

Neo4jClient/Execution/ExecutionConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ public ExecutionConfiguration()
2222
public Guid ResourceManagerId { get; set; }
2323
public string Realm { get; set; }
2424
public EncryptionLevel? EncryptionLevel { get; set; }
25+
public bool SerializeNullValues { get; set; }
2526
}
2627
}

Neo4jClient/Neo4jDriverExtensions.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Neo4j.Driver;
99
using Neo4jClient.Cypher;
1010
using Neo4jClient.Serialization;
11-
using Neo4jClient.Transactions;
1211
using Newtonsoft.Json;
1312
using Newtonsoft.Json.Serialization;
1413

@@ -100,9 +99,20 @@ private static object Serialize(object value, IList<JsonConverter> converters, I
10099

101100
private static object SerializeObject(Type type, object value, IList<JsonConverter> converters, IGraphClient gc)
102101
{
103-
return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
104-
.Where(pi => !(pi.GetIndexParameters().Any() || pi.IsDefined(typeof(JsonIgnoreAttribute)) || pi.IsDefined(typeof(Neo4jIgnoreAttribute))))
105-
.ToDictionary(pi => GetPropertyName(pi.Name, gc, type), pi => Serialize(pi.GetValue(value), converters, gc, pi.CustomAttributes));
102+
var serialized = new Dictionary<string, object>();
103+
foreach (var propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
104+
.Where(pi => !(pi.GetIndexParameters().Any() || pi.IsDefined(typeof(JsonIgnoreAttribute)) || pi.IsDefined(typeof(Neo4jIgnoreAttribute)))))
105+
{
106+
var propertyName = GetPropertyName(propertyInfo.Name, gc, type);
107+
var propertyValue = propertyInfo.GetValue(value);
108+
109+
if (propertyValue != null || gc.ExecutionConfiguration.SerializeNullValues)
110+
{
111+
serialized.Add(propertyName, Serialize(propertyValue, converters, gc, propertyInfo.CustomAttributes));
112+
}
113+
}
114+
115+
return serialized;
106116
}
107117

108118
private static string GetPropertyName(string argName, IGraphClient gc, Type type)
@@ -181,7 +191,10 @@ private static object SerializeDictionary(Type type, object value, IList<JsonCon
181191
string key = item.Key;
182192
object entry = item.Value;
183193

184-
serialized[key] = Serialize(entry, converters, gc);
194+
if (entry != null || gc.ExecutionConfiguration.SerializeNullValues)
195+
{
196+
serialized[key] = Serialize(entry, converters, gc);
197+
}
185198
}
186199

187200
return serialized;

0 commit comments

Comments
 (0)