使用 Newtonsoft.Json 合并对象时,如何忽略空字符串值?
When merging objects using Newtonsoft.Json, how do you ignore empty string values?
我有一个在 C# 中定义为 class 的数据模型。我需要使用 JObject.Merge 合并两个对象,但在一个特定的 属性 的情况下,我需要忽略第二个对象中的空字符串值,类似于忽略空值的方式。是否有现有属性 属性,或者我是否需要以某种方式编写自己的属性?
void Main()
{
string json1 = @"{ Foo: ""foo1"", Bar: ""bar1"" }";
string json2 = @"{ Foo: ""foo2"", Bar: null }";
string json3 = @"{ Foo: ""foo3"", Bar: """" }";
var model1 = JObject.Parse(json1);
var model2 = JObject.Parse(json2);
model1.Merge(model2);
model1.Dump();
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
model1.Merge(model2);
model1.Dump();
}
public class Model
{
[JsonProperty("foo")]
public string Foo { get ; set; }
[JsonProperty("bar", NullValueHandling = NullValueHandling.Ignore)]
public string Bar { get ; set; }
}
Output (1): Model.Bar = "bar1"
Output (2): Model.Bar = "";
Desired output of (2): Model.Bar = "bar1"
编辑:好的,我意识到无法应用属性,因为我只需要使用原始 json 字符串作为输入。这主要是因为我的 classes 具有带默认值的属性。将 classes 与空值合并会触发默认值并最终覆盖原始值,这是我不想要的。我需要能够获取 class 的部分 json 表示并更新原件。抱歉,如果从一开始就不清楚。我会更新我最终做出的回答。
您可以使用以下扩展方法从要合并的 JObject
中删除具有空字符串值的属性:
public static class JsonExtensions
{
public static void RemovePropertiesByValue(this JToken root, Predicate<JValue> filter)
{
var nulls = root.DescendantsAndSelf().OfType<JValue>().Where(v => v.Parent is JProperty && filter(v)).ToList();
foreach (var value in nulls)
{
var parent = (JProperty)value.Parent;
parent.Remove();
}
}
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new [] { node };
}
}
然后像这样使用它:
model2.RemovePropertiesByValue(v => v.Type == JTokenType.String && string.IsNullOrEmpty((string)v.Value));
请注意,这不会从数组中删除空字符串,因为那样会弄乱数组索引,从而导致合并。你也需要吗?
我操纵了 JObject 字典键来处理特定条目。我觉得很脏,但它有效。我想不出 "good" 方法来做到这一点。
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
IDictionary<string, JToken> dictionary = model2;
dictionary["Bar"] = string.IsNullOrEmpty((string)dictionary["Bar"])
? null
: dictionary["Bar"];
model1.Merge(model2);
我有一个在 C# 中定义为 class 的数据模型。我需要使用 JObject.Merge 合并两个对象,但在一个特定的 属性 的情况下,我需要忽略第二个对象中的空字符串值,类似于忽略空值的方式。是否有现有属性 属性,或者我是否需要以某种方式编写自己的属性?
void Main()
{
string json1 = @"{ Foo: ""foo1"", Bar: ""bar1"" }";
string json2 = @"{ Foo: ""foo2"", Bar: null }";
string json3 = @"{ Foo: ""foo3"", Bar: """" }";
var model1 = JObject.Parse(json1);
var model2 = JObject.Parse(json2);
model1.Merge(model2);
model1.Dump();
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
model1.Merge(model2);
model1.Dump();
}
public class Model
{
[JsonProperty("foo")]
public string Foo { get ; set; }
[JsonProperty("bar", NullValueHandling = NullValueHandling.Ignore)]
public string Bar { get ; set; }
}
Output (1): Model.Bar = "bar1"
Output (2): Model.Bar = "";
Desired output of (2): Model.Bar = "bar1"
编辑:好的,我意识到无法应用属性,因为我只需要使用原始 json 字符串作为输入。这主要是因为我的 classes 具有带默认值的属性。将 classes 与空值合并会触发默认值并最终覆盖原始值,这是我不想要的。我需要能够获取 class 的部分 json 表示并更新原件。抱歉,如果从一开始就不清楚。我会更新我最终做出的回答。
您可以使用以下扩展方法从要合并的 JObject
中删除具有空字符串值的属性:
public static class JsonExtensions
{
public static void RemovePropertiesByValue(this JToken root, Predicate<JValue> filter)
{
var nulls = root.DescendantsAndSelf().OfType<JValue>().Where(v => v.Parent is JProperty && filter(v)).ToList();
foreach (var value in nulls)
{
var parent = (JProperty)value.Parent;
parent.Remove();
}
}
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new [] { node };
}
}
然后像这样使用它:
model2.RemovePropertiesByValue(v => v.Type == JTokenType.String && string.IsNullOrEmpty((string)v.Value));
请注意,这不会从数组中删除空字符串,因为那样会弄乱数组索引,从而导致合并。你也需要吗?
我操纵了 JObject 字典键来处理特定条目。我觉得很脏,但它有效。我想不出 "good" 方法来做到这一点。
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
IDictionary<string, JToken> dictionary = model2;
dictionary["Bar"] = string.IsNullOrEmpty((string)dictionary["Bar"])
? null
: dictionary["Bar"];
model1.Merge(model2);