diff --git a/Src/Newtonsoft.Json.Tests/Linq/MergeTests.cs b/Src/Newtonsoft.Json.Tests/Linq/MergeTests.cs index 0701d07c..487ea8d7 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/MergeTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/MergeTests.cs @@ -569,5 +569,36 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("name1", p.Name); Assert.AreEqual(0, p.Count); } + + [Test] + public void MergeNullValue() + { + var source = new JObject + { + {"Property1", "value"}, + {"Property2", new JObject()}, + {"Property3", JValue.CreateNull()}, + {"Property4", JValue.CreateUndefined()}, + {"Property5", new JArray()} + }; + + var patch = JObject.Parse("{Property1: null, Property2: null, Property3: null, Property4: null, Property5: null}"); + + source.Merge(patch, new JsonMergeSettings + { + MergeNullValueHandling = MergeNullValueHandling.Merge + }); + + Assert.IsNotNull(source["Property1"]); + Assert.AreEqual(JTokenType.Null, source["Property1"].Type); + Assert.IsNotNull(source["Property2"]); + Assert.AreEqual(JTokenType.Null, source["Property2"].Type); + Assert.IsNotNull(source["Property3"]); + Assert.AreEqual(JTokenType.Null, source["Property3"].Type); + Assert.IsNotNull(source["Property4"]); + Assert.AreEqual(JTokenType.Null, source["Property4"].Type); + Assert.IsNotNull(source["Property5"]); + Assert.AreEqual(JTokenType.Null, source["Property5"].Type); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 431282fb..7e7d49c1 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -187,7 +187,7 @@ namespace Newtonsoft.Json.Linq JContainer existingContainer = existingProperty.Value as JContainer; if (existingContainer == null) { - if (contentItem.Value.Type != JTokenType.Null) + if (contentItem.Value.Type != JTokenType.Null || settings?.MergeNullValueHandling == MergeNullValueHandling.Merge) { existingProperty.Value = contentItem.Value; } diff --git a/Src/Newtonsoft.Json/Linq/JsonMergeSettings.cs b/Src/Newtonsoft.Json/Linq/JsonMergeSettings.cs index c86175c5..9542095a 100644 --- a/Src/Newtonsoft.Json/Linq/JsonMergeSettings.cs +++ b/Src/Newtonsoft.Json/Linq/JsonMergeSettings.cs @@ -8,6 +8,7 @@ namespace Newtonsoft.Json.Linq public class JsonMergeSettings { private MergeArrayHandling _mergeArrayHandling; + private MergeNullValueHandling _mergeNullValueHandling; /// /// Gets or sets the method used when merging JSON arrays. @@ -26,5 +27,23 @@ namespace Newtonsoft.Json.Linq _mergeArrayHandling = value; } } + + /// + /// Gets or sets how how null value properties are merged. + /// + /// How null value properties are merged. + public MergeNullValueHandling MergeNullValueHandling + { + get { return _mergeNullValueHandling; } + set + { + if (value < MergeNullValueHandling.Ignore || value > MergeNullValueHandling.Merge) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + + _mergeNullValueHandling = value; + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/MergeNullValueHandling.cs b/Src/Newtonsoft.Json/Linq/MergeNullValueHandling.cs new file mode 100644 index 00000000..30d3d845 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/MergeNullValueHandling.cs @@ -0,0 +1,21 @@ +using System; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Specifies how null value properties are merged. + /// + [Flags] + public enum MergeNullValueHandling + { + /// + /// The content's null value properties will be ignored during merging. + /// + Ignore = 0, + + /// + /// The content's null value properties will be merged. + /// + Merge = 1 + } +} diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 5e9b73de..b63455d0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -125,6 +125,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index e970986c..09ea61c9 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj index 24b34563..e4cf0e32 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index d3e5d2cd..2ff19dd6 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -136,6 +136,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj index eae7deab..762db4d0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj @@ -141,6 +141,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index b87c7e45..d69978da 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -140,6 +140,7 @@ +