diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs index a1356f82..ccbe8fde 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs @@ -1770,11 +1770,11 @@ third line", jsonTextReader.Value); } "; - JsonLoadSettings settings = new JsonLoadSettings { DuplicatePropertyNamesHandling = DuplicatePropertyNamesHandling.Throw }; + JsonLoadSettings settings = new JsonLoadSettings { DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Error }; + JsonTextReader reader = new JsonTextReader(new StringReader(json)); - await ExceptionAssert.ThrowsAsync( async () => await JToken.ReadFromAsync(reader, settings)); + await ExceptionAssert.ThrowsAsync(async () => await JToken.ReadFromAsync(reader, settings)); } } - } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs index c2206a63..ce96f3d3 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs @@ -1761,12 +1761,11 @@ third line", jsonTextReader.Value); } "; - JsonLoadSettings settings = new JsonLoadSettings {DuplicatePropertyNamesHandling = DuplicatePropertyNamesHandling.Throw}; + JsonLoadSettings settings = new JsonLoadSettings {DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Error}; - ExceptionAssert.Throws(() => JObject.Parse(json, settings)); + JsonTextReader reader = new JsonTextReader(new StringReader(json)); ExceptionAssert.Throws(() => { - JsonTextReader reader = new JsonTextReader(new StringReader(json)); JToken.ReadFrom(reader, settings); }); } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectAsyncTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectAsyncTests.cs index 4a440088..1bbdf1c3 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectAsyncTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectAsyncTests.cs @@ -162,6 +162,39 @@ namespace Newtonsoft.Json.Tests.Linq await JToken.ReadFromAsync(reader); }, "Unexpected end of content while loading JObject. Path 'short.error.code', line 6, position 14."); } + + [Test] + public async Task ParseMultipleProperties_EmptySettingsAsync() + { + string json = @"{ + ""Name"": ""Name1"", + ""Name"": ""Name2"" + }"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + JObject o = (JObject)await JToken.ReadFromAsync(reader, new JsonLoadSettings()); + string value = (string)o["Name"]; + + Assert.AreEqual("Name2", value); + } + + [Test] + public async Task ParseMultipleProperties_IgnoreDuplicateSettingAsync() + { + string json = @"{ + ""Name"": ""Name1"", + ""Name"": ""Name2"" + }"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + JObject o = (JObject)await JToken.ReadFromAsync(reader, new JsonLoadSettings + { + DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Ignore + }); + string value = (string)o["Name"]; + + Assert.AreEqual("Name1", value); + } } } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index 3e481989..4497f745 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1604,6 +1604,37 @@ Parameter name: arrayIndex"); Assert.AreEqual("Name2", value); } + [Test] + public void ParseMultipleProperties_EmptySettings() + { + string json = @"{ + ""Name"": ""Name1"", + ""Name"": ""Name2"" + }"; + + JObject o = JObject.Parse(json, new JsonLoadSettings()); + string value = (string)o["Name"]; + + Assert.AreEqual("Name2", value); + } + + [Test] + public void ParseMultipleProperties_IgnoreDuplicateSetting() + { + string json = @"{ + ""Name"": ""Name1"", + ""Name"": ""Name2"" + }"; + + JObject o = JObject.Parse(json, new JsonLoadSettings + { + DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Ignore + }); + string value = (string)o["Name"]; + + Assert.AreEqual("Name1", value); + } + #if !(PORTABLE || DNXCORE50 || PORTABLE40) || NETSTANDARD2_0 [Test] public void WriteObjectNullDBNullValue() diff --git a/Src/Newtonsoft.Json/Linq/DuplicatePropertyNamesHandling.cs b/Src/Newtonsoft.Json/Linq/DuplicatePropertyNameHandling.cs similarity index 73% rename from Src/Newtonsoft.Json/Linq/DuplicatePropertyNamesHandling.cs rename to Src/Newtonsoft.Json/Linq/DuplicatePropertyNameHandling.cs index 08b24ed7..43e27fc8 100644 --- a/Src/Newtonsoft.Json/Linq/DuplicatePropertyNamesHandling.cs +++ b/Src/Newtonsoft.Json/Linq/DuplicatePropertyNameHandling.cs @@ -28,15 +28,19 @@ namespace Newtonsoft.Json.Linq /// /// Specifies how duplicate property names are handled when loading JSON. /// - public enum DuplicatePropertyNamesHandling + public enum DuplicatePropertyNameHandling { /// - /// Ignore duplicate properties, use only last value (default) + /// Replace the existing value when there is a duplicate property. The value of the last property in the JSON object will be used. /// - Ignore = 0, + Replace = 0, /// - /// Throw exception + /// Ignore the new value when there is a duplicate property. The value of the first property in the JSON object will be used. /// - Throw = 1 + Ignore = 1, + /// + /// Throw a when a duplicate property is encountered. + /// + Error = 2 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.Async.cs b/Src/Newtonsoft.Json/Linq/JContainer.Async.cs index cd00d47f..324c62a1 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.Async.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.Async.cs @@ -148,25 +148,15 @@ namespace Newtonsoft.Json.Linq parent.Add(v); break; case JsonToken.PropertyName: - string propertyName = reader.Value.ToString(); - JProperty property = new JProperty(propertyName); - property.SetLineInfo(lineInfo, settings); - JObject parentObject = (JObject)parent; - // handle multiple properties with the same name in JSON - JProperty existingPropertyWithName = parentObject.Property(propertyName); - if (existingPropertyWithName == null) + JProperty property = ReadProperty(reader, settings, lineInfo, parent); + if (property != null) { - parent.Add(property); + parent = property; } else { - if (settings != null && settings.DuplicatePropertyNamesHandling == DuplicatePropertyNamesHandling.Throw) - { - throw JsonException.Create(lineInfo, property.Path, $"Property with the same name ('{propertyName}') already exists"); - } - existingPropertyWithName.Replace(property); + await reader.SkipAsync(); } - parent = property; break; default: throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 4fa19b5f..05b8e62b 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -837,25 +837,15 @@ namespace Newtonsoft.Json.Linq parent.Add(v); break; case JsonToken.PropertyName: - string propertyName = r.Value.ToString(); - JProperty property = new JProperty(propertyName); - property.SetLineInfo(lineInfo, settings); - JObject parentObject = (JObject)parent; - // handle multiple properties with the same name in JSON - JProperty existingPropertyWithName = parentObject.Property(propertyName); - if (existingPropertyWithName == null) + JProperty property = ReadProperty(r, settings, lineInfo, parent); + if (property != null) { - parent.Add(property); + parent = property; } else { - if (settings != null && settings.DuplicatePropertyNamesHandling == DuplicatePropertyNamesHandling.Throw) - { - throw JsonException.Create(lineInfo, property.Path, $"Property with the same name ('{propertyName}') already exists"); - } - existingPropertyWithName.Replace(property); + r.Skip(); } - parent = property; break; default: throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType)); @@ -863,6 +853,40 @@ namespace Newtonsoft.Json.Linq } while (r.Read()); } + private static JProperty ReadProperty(JsonReader r, JsonLoadSettings settings, IJsonLineInfo lineInfo, JContainer parent) + { + DuplicatePropertyNameHandling duplicatePropertyNameHandling = settings?.DuplicatePropertyNameHandling ?? DuplicatePropertyNameHandling.Replace; + + JObject parentObject = (JObject)parent; + string propertyName = r.Value.ToString(); + JProperty existingPropertyWithName = parentObject.Property(propertyName); + if (existingPropertyWithName != null) + { + if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Ignore) + { + return null; + } + else if (duplicatePropertyNameHandling == DuplicatePropertyNameHandling.Error) + { + throw JsonReaderException.Create(r, "Property with the name '{0}' already exists in the current JSON object.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + } + + JProperty property = new JProperty(propertyName); + property.SetLineInfo(lineInfo, settings); + // handle multiple properties with the same name in JSON + if (existingPropertyWithName == null) + { + parent.Add(property); + } + else + { + existingPropertyWithName.Replace(property); + } + + return property; + } + internal int ContentsHashCode() { int hashCode = 0; diff --git a/Src/Newtonsoft.Json/Linq/JsonLoadSettings.cs b/Src/Newtonsoft.Json/Linq/JsonLoadSettings.cs index 4204ffac..0a450d8a 100644 --- a/Src/Newtonsoft.Json/Linq/JsonLoadSettings.cs +++ b/Src/Newtonsoft.Json/Linq/JsonLoadSettings.cs @@ -9,7 +9,7 @@ namespace Newtonsoft.Json.Linq { private CommentHandling _commentHandling; private LineInfoHandling _lineInfoHandling; - private DuplicatePropertyNamesHandling _duplicatePropertyNamesHandling; + private DuplicatePropertyNameHandling _duplicatePropertyNameHandling; /// /// Initializes a new instance of the class. @@ -18,11 +18,12 @@ namespace Newtonsoft.Json.Linq { _lineInfoHandling = LineInfoHandling.Load; _commentHandling = CommentHandling.Ignore; - _duplicatePropertyNamesHandling = DuplicatePropertyNamesHandling.Ignore; + _duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace; } /// /// Gets or sets how JSON comments are handled when loading JSON. + /// The default value is . /// /// The JSON comment handling. public CommentHandling CommentHandling @@ -41,6 +42,7 @@ namespace Newtonsoft.Json.Linq /// /// Gets or sets how JSON line info is handled when loading JSON. + /// The default value is . /// /// The JSON line info handling. public LineInfoHandling LineInfoHandling @@ -58,20 +60,21 @@ namespace Newtonsoft.Json.Linq } /// - /// Gets or sets how duplicate property names are handled when loading JSON. + /// Gets or sets how duplicate property names in JSON objects are handled when loading JSON. + /// The default value is . /// - /// The JSON duplicate property names handling. - public DuplicatePropertyNamesHandling DuplicatePropertyNamesHandling + /// The JSON duplicate property name handling. + public DuplicatePropertyNameHandling DuplicatePropertyNameHandling { - get => this._duplicatePropertyNamesHandling; + get => _duplicatePropertyNameHandling; set { - if (value< DuplicatePropertyNamesHandling.Ignore|| value> DuplicatePropertyNamesHandling.Throw) + if (value < DuplicatePropertyNameHandling.Ignore || value > DuplicatePropertyNameHandling.Error) { throw new ArgumentOutOfRangeException(nameof(value)); } - this._duplicatePropertyNamesHandling = value; + _duplicatePropertyNameHandling = value; } } } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 5891646d..2c20b8c4 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -34,7 +34,7 @@ - + Json.NET diff --git a/Src/global.json b/Src/global.json index eb4b2830..21e27ced 100644 --- a/Src/global.json +++ b/Src/global.json @@ -1,5 +1,4 @@ { - "projects": [ "src" ], "sdk": { "version": "2.1.500" }