Clean up DuplicatePropertyNameHandling feature

This commit is contained in:
James Newton-King
2018-11-24 21:25:20 +13:00
parent a1f1b0aa86
commit 88dae52165
10 changed files with 132 additions and 49 deletions
@@ -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<JsonException>( async () => await JToken.ReadFromAsync(reader, settings));
await ExceptionAssert.ThrowsAsync<JsonReaderException>(async () => await JToken.ReadFromAsync(reader, settings));
}
}
}
#endif
@@ -1761,12 +1761,11 @@ third line", jsonTextReader.Value);
}
";
JsonLoadSettings settings = new JsonLoadSettings {DuplicatePropertyNamesHandling = DuplicatePropertyNamesHandling.Throw};
JsonLoadSettings settings = new JsonLoadSettings {DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Error};
ExceptionAssert.Throws<JsonException>(() => JObject.Parse(json, settings));
JsonTextReader reader = new JsonTextReader(new StringReader(json));
ExceptionAssert.Throws<JsonException>(() =>
{
JsonTextReader reader = new JsonTextReader(new StringReader(json));
JToken.ReadFrom(reader, settings);
});
}
@@ -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);
}
}
}
@@ -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()
@@ -28,15 +28,19 @@ namespace Newtonsoft.Json.Linq
/// <summary>
/// Specifies how duplicate property names are handled when loading JSON.
/// </summary>
public enum DuplicatePropertyNamesHandling
public enum DuplicatePropertyNameHandling
{
/// <summary>
/// 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.
/// </summary>
Ignore = 0,
Replace = 0,
/// <summary>
/// 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.
/// </summary>
Throw = 1
Ignore = 1,
/// <summary>
/// Throw a <see cref="JsonReaderException"/> when a duplicate property is encountered.
/// </summary>
Error = 2
}
}
+4 -14
View File
@@ -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));
+38 -14
View File
@@ -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;
+11 -8
View File
@@ -9,7 +9,7 @@ namespace Newtonsoft.Json.Linq
{
private CommentHandling _commentHandling;
private LineInfoHandling _lineInfoHandling;
private DuplicatePropertyNamesHandling _duplicatePropertyNamesHandling;
private DuplicatePropertyNameHandling _duplicatePropertyNameHandling;
/// <summary>
/// Initializes a new instance of the <see cref="JsonLoadSettings"/> class.
@@ -18,11 +18,12 @@ namespace Newtonsoft.Json.Linq
{
_lineInfoHandling = LineInfoHandling.Load;
_commentHandling = CommentHandling.Ignore;
_duplicatePropertyNamesHandling = DuplicatePropertyNamesHandling.Ignore;
_duplicatePropertyNameHandling = DuplicatePropertyNameHandling.Replace;
}
/// <summary>
/// Gets or sets how JSON comments are handled when loading JSON.
/// The default value is <see cref="CommentHandling.Ignore" />.
/// </summary>
/// <value>The JSON comment handling.</value>
public CommentHandling CommentHandling
@@ -41,6 +42,7 @@ namespace Newtonsoft.Json.Linq
/// <summary>
/// Gets or sets how JSON line info is handled when loading JSON.
/// The default value is <see cref="LineInfoHandling.Load" />.
/// </summary>
/// <value>The JSON line info handling.</value>
public LineInfoHandling LineInfoHandling
@@ -58,20 +60,21 @@ namespace Newtonsoft.Json.Linq
}
/// <summary>
/// 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 <see cref="DuplicatePropertyNameHandling.Replace" />.
/// </summary>
/// <value>The JSON duplicate property names handling.</value>
public DuplicatePropertyNamesHandling DuplicatePropertyNamesHandling
/// <value>The JSON duplicate property name handling.</value>
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;
}
}
}
+1 -1
View File
@@ -34,7 +34,7 @@
<None Include="..\..\LICENSE.md" Pack="true" PackagePath="LICENSE.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-62925-02" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63127-02" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net45'">
<AssemblyTitle>Json.NET</AssemblyTitle>
-1
View File
@@ -1,5 +1,4 @@
{
"projects": [ "src" ],
"sdk": {
"version": "2.1.500"
}