C# 使用AutoMapper、Json.NET或其他库使用Json字符串中的更改更新父属性和子属性
我有一个场景,给我一个json字符串,其中只包含用户为给定父/子对象修改的属性 当给定这个字符串时,我需要加载原始对象并使用json字符串中的更改对其进行更新 示例类C# 使用AutoMapper、Json.NET或其他库使用Json字符串中的更改更新父属性和子属性,c#,reflection,json.net,automapper,C#,Reflection,Json.net,Automapper,我有一个场景,给我一个json字符串,其中只包含用户为给定父/子对象修改的属性 当给定这个字符串时,我需要加载原始对象并使用json字符串中的更改对其进行更新 示例类 public class ParentObject { public int ID { get; set; } public string Prop1 { get; set; } public string Prop2 { get; set; } public List<ChildObject
public class ParentObject
{
public int ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<ChildObject> Children { get; set; }
}
public class ChildObject
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
然后,我必须用更改的属性“更新”“原始”对象
我可以使用反射来迭代“原始”对象的属性,然后将这些属性与Json字符串进行比较,但我想知道是否还有其他像AutoMapper这样的库可以更容易地做到这一点
Newton Json.NET合并看起来很有希望。自动映射代码--大部分工作正常
此代码“几乎”的工作原理是,它确实映射父级和子级,但如果子级具有与父级同名的属性,则此代码将失败。
AKA ID字段未映射到子项上
// Configure the mapping
Mapper.Initialize(cfg => cfg.CreateMap<ExpandoObject, ParentObject>());
// Convert the JSon into a dynamic list
var sources = JsonConvert.DeserializeObject<List<dynamic>>(modifiedProperties);
// Iterate the list of changes
foreach (dynamic source in sources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source.ID);
// Simulate a DB call to get the original object
var destination = originalObject;
// Map the objects together
var realDestination = Mapper.Map(source, destination);
// ** OUTPUT **
// realDestination.ID = 1 <-- THIS IS CORRECT
// realDestination.Prop1 = "Prop 1 Changed" <-- THIS IS CORRECT
// realDestination.Prop2 = "Prop 2 Original" <-- THIS IS CORRECT
// realDestination.Children.FirstOrDefault().ID = 0 <-- THIS SHOULD BE 22 NOT 0
// realDestination.Children.FirstOrDefault().PropA = "Prop A Changed" <-- THIS IS CORRECT
// realDestination.Children.FirstOrDefault().PropB = null <-- THIS IS CORRECT
}
//配置映射
初始化(cfg=>cfg.CreateMap());
//将JSon转换为动态列表
var sources=JsonConvert.DeserializeObject(modifiedProperties);
//迭代更改列表
foreach(源中的动态源)
{
//获取源ID以从数据库中提取记录
var sourceID=Convert.ToInt32(source.ID);
//模拟DB调用以获取原始对象
var destination=原始对象;
//将对象映射到一起
var realDestination=Mapper.Map(源、目标);
//**产出**
//realDestination.ID=1我让AutoMapper和Json.NET都工作了。
下面是我正在使用的代码和测试数据
public class ParentObject
{
public int ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<ChildObject> Children { get; set; }
}
public class ChildObject
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
// Original object before modifications
var originalObject = new ParentObject
{
ID = 1,
Prop1 = "Prop 1 Original",
Prop2 = "Prop 2 Original",
Children = new List<ChildObject>() { new ChildObject {
ID = 22,
PropA = "Prop A Original"}
}
};
// Get the modified properties
string modifiedProperties =
@"[{
""ID"": 1,
""Prop1"": ""Prop 1 Changed"",
""Children"": [{
""ID"": 22,
""PropA"": ""Prop A Changed""
}]
}]";
/* AUTOMAPPER CODE */
// Convert the JSon into a dynamic list for AUTOMAPPER
var mapperSources = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ExpandoObject>>(modifiedProperties);
// Initialize mapper
AutoMapper.Mapper.Initialize(cfg => { });
// Iterate the list of changes for AUTOMAPPER
foreach (dynamic source in mapperSources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source.ID);
// Simulate a DB call to get the original object
var destination = originalObject;
// Map the objects together
var realDestination = AutoMapper.Mapper.Map(source, destination);
}
/* JSON.NET CODE */
// Convert the JSon into a dynamic list for JSON.NET
Newtonsoft.Json.Linq.JArray sources = Newtonsoft.Json.Linq.JArray.Parse(modifiedProperties);
// Iterate the list of changes for JSON.NET
foreach (Newtonsoft.Json.Linq.JObject source in sources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source["ID"]);
// Simulate a DB call to get the original object
var destination = originalObject;
// convert the object into a JObject for merge
Newtonsoft.Json.Linq.JObject tempDestination = Newtonsoft.Json.Linq.JObject.FromObject(destination);
// Map the objects together
tempDestination.Merge(source, new Newtonsoft.Json.Linq.JsonMergeSettings
{
// Using merge
MergeArrayHandling = Newtonsoft.Json.Linq.MergeArrayHandling.Merge
});
var realDestination = tempDestination.ToObject<ParentObject>();
}
/* EXPECTED OUTPUT
realDestination.ID = 1
realDestination.Prop1 = "Prop 1 Changed"
realDestination.Prop2 = "Prop 2 Original"
realDestination.Children.FirstOrDefault().ID = 22
realDestination.Children.FirstOrDefault().PropA = "Prop A Changed"
realDestination.Children.FirstOrDefault().PropB = null
*/
公共类ParentObject
{
公共int ID{get;set;}
公共字符串Prop1{get;set;}
公共字符串Prop2{get;set;}
公共列表子项{get;set;}
}
公共类子对象
{
公共int ID{get;set;}
公共字符串PropA{get;set;}
公共字符串PropB{get;set;}
}
//修改前的原始对象
var originalObject=新的父对象
{
ID=1,
Prop1=“Prop1原件”,
Prop2=“Prop 2原件”,
Children=new List(){new ChildObject{
ID=22,
PropA=“Prop A Original”}
}
};
//获取修改后的属性
字符串修饰符属性=
@"[{
“ID”:1,
“Prop1”:“Prop1已更改”,
“儿童”:[{
“”ID“”:22,
“PropA”:“Prop A已更改”
}]
}]";
/*自动映射代码*/
//将JSon转换为AUTOMAPPER的动态列表
var mapperSources=Newtonsoft.Json.JsonConvert.DeserializeObject(modifiedProperties);
//初始化映射器
AutoMapper.Mapper.Initialize(cfg=>{});
//迭代AUTOMAPPER的更改列表
foreach(mapperSources中的动态源)
{
//获取源ID以从数据库中提取记录
var sourceID=Convert.ToInt32(source.ID);
//模拟DB调用以获取原始对象
var destination=原始对象;
//将对象映射到一起
var realDestination=AutoMapper.Mapper.Map(源、目标);
}
/*JSON.NET代码*/
//将JSon转换为JSon.NET的动态列表
Newtonsoft.Json.Linq.JArray sources=Newtonsoft.Json.Linq.JArray.Parse(modifiedProperties);
//迭代JSON.NET的更改列表
foreach(源代码中的Newtonsoft.Json.Linq.JObject源代码)
{
//获取源ID以从数据库中提取记录
var sourceID=Convert.ToInt32(source[“ID”]);
//模拟DB调用以获取原始对象
var destination=原始对象;
//将对象转换为要合并的作业对象
Newtonsoft.Json.Linq.JObject tempDestination=Newtonsoft.Json.Linq.JObject.FromObject(目的地);
//将对象映射到一起
tempDestination.Merge(源代码,新的Newtonsoft.Json.Linq.JsonMergeSettings
{
//使用合并
MergeArrayHandling=Newtonsoft.Json.Linq.MergeArrayHandling.Merge
});
var realDestination=tempDestination.ToObject();
}
/*预期产量
realDestination.ID=1
realditudio.Prop1=“Prop 1已更改”
realditudio.Prop2=“Prop 2原件”
realDestination.Children.FirstOrDefault().ID=22
realDestination.Children.FirstOrDefault().PropA=“道具A已更改”
realDestination.Children.FirstOrDefault().PropB=null
*/
我运行了一个.NET秒表,以下是我所知道的最好的“性能”结果。
如果有人注意到我的测试代码中有错误,请告诉我
Stopwatch sw = new Stopwatch();
for (int c = 0; c < 4; c++)
{
sw.Restart();
for (int i = 0; i < 500000; i++)
{
/* MAPPING CODE HERE */
}
sw.Stop();
Console.WriteLine("Elapsed = {0}", sw.Elapsed);
}
/* STOPWATCH RESULTS
Elapsed AUTOMAPPER = 00:00:16.5727406
Elapsed AUTOMAPPER = 00:00:16.8015508
Elapsed AUTOMAPPER = 00:00:14.6642556
Elapsed AUTOMAPPER = 00:00:13.9644693
Elapsed JSON.NET = 00:00:18.8097409
Elapsed JSON.NET = 00:00:17.6313278
Elapsed JSON.NET = 00:00:18.6343426
Elapsed JSON.NET = 00:00:18.8855448
*/
Stopwatch sw=新秒表();
对于(int c=0;c<4;c++)
{
sw.Restart();
对于(int i=0;i<500000;i++)
{
/*这里映射代码*/
}
sw.Stop();
WriteLine(“运行时间={0}”,sw.appeased);
}
/*秒表结果
经过的自动映射=00:00:16.5727406
已用自动映射=00:00:16.8015508
已用自动映射=00:00:14.6642556
已用自动映射=00:00:13.9644693
已用JSON.NET=00:00:18.8097409
已用JSON.NET=00:00:17.6313278
已用JSON.NET=00:00:18.6343426
已用JSON.NET=00:00:18.8855448
*/
AutoMapper是个好主意。说“它不起作用”是没有帮助的。显示你使用的代码并解释什么不起作用。可能对你有用。@Equalsk我已经添加了AutoMapper代码,但我不确定AutoMapper是正确的路径,因为我没有看到任何关于它的文档能够“合并”只是更改。再说一遍,你说“不起作用”,但不说实际发生了什么……我手头没有编写测试的工具,但我假设您需要类似于CreateMap().ForAllMembers的东西(o=>o.Condition((src,dest,srcMember)=>srcMember!=null));
假设属性是引用类型。由于您没有显示类或示例值,我不得不猜测。@Equalsk您的代码看起来很有希望,但当我尝试时,我得到了一个新的“空”父对象
public class ParentObject
{
public int ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<ChildObject> Children { get; set; }
}
public class ChildObject
{
public int ID { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
// Original object before modifications
var originalObject = new ParentObject
{
ID = 1,
Prop1 = "Prop 1 Original",
Prop2 = "Prop 2 Original",
Children = new List<ChildObject>() { new ChildObject {
ID = 22,
PropA = "Prop A Original"}
}
};
// Get the modified properties
string modifiedProperties =
@"[{
""ID"": 1,
""Prop1"": ""Prop 1 Changed"",
""Children"": [{
""ID"": 22,
""PropA"": ""Prop A Changed""
}]
}]";
/* AUTOMAPPER CODE */
// Convert the JSon into a dynamic list for AUTOMAPPER
var mapperSources = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ExpandoObject>>(modifiedProperties);
// Initialize mapper
AutoMapper.Mapper.Initialize(cfg => { });
// Iterate the list of changes for AUTOMAPPER
foreach (dynamic source in mapperSources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source.ID);
// Simulate a DB call to get the original object
var destination = originalObject;
// Map the objects together
var realDestination = AutoMapper.Mapper.Map(source, destination);
}
/* JSON.NET CODE */
// Convert the JSon into a dynamic list for JSON.NET
Newtonsoft.Json.Linq.JArray sources = Newtonsoft.Json.Linq.JArray.Parse(modifiedProperties);
// Iterate the list of changes for JSON.NET
foreach (Newtonsoft.Json.Linq.JObject source in sources)
{
// Get the source ID to pull the record from the database
var sourceID = Convert.ToInt32(source["ID"]);
// Simulate a DB call to get the original object
var destination = originalObject;
// convert the object into a JObject for merge
Newtonsoft.Json.Linq.JObject tempDestination = Newtonsoft.Json.Linq.JObject.FromObject(destination);
// Map the objects together
tempDestination.Merge(source, new Newtonsoft.Json.Linq.JsonMergeSettings
{
// Using merge
MergeArrayHandling = Newtonsoft.Json.Linq.MergeArrayHandling.Merge
});
var realDestination = tempDestination.ToObject<ParentObject>();
}
/* EXPECTED OUTPUT
realDestination.ID = 1
realDestination.Prop1 = "Prop 1 Changed"
realDestination.Prop2 = "Prop 2 Original"
realDestination.Children.FirstOrDefault().ID = 22
realDestination.Children.FirstOrDefault().PropA = "Prop A Changed"
realDestination.Children.FirstOrDefault().PropB = null
*/
Stopwatch sw = new Stopwatch();
for (int c = 0; c < 4; c++)
{
sw.Restart();
for (int i = 0; i < 500000; i++)
{
/* MAPPING CODE HERE */
}
sw.Stop();
Console.WriteLine("Elapsed = {0}", sw.Elapsed);
}
/* STOPWATCH RESULTS
Elapsed AUTOMAPPER = 00:00:16.5727406
Elapsed AUTOMAPPER = 00:00:16.8015508
Elapsed AUTOMAPPER = 00:00:14.6642556
Elapsed AUTOMAPPER = 00:00:13.9644693
Elapsed JSON.NET = 00:00:18.8097409
Elapsed JSON.NET = 00:00:17.6313278
Elapsed JSON.NET = 00:00:18.6343426
Elapsed JSON.NET = 00:00:18.8855448
*/