C# 如何通过Linq表达式序列化对象
我想用linq表达式序列化一个对象。让我们假设,我有以下课程:C# 如何通过Linq表达式序列化对象,c#,linq,nhibernate,json.net,expression,C#,Linq,Nhibernate,Json.net,Expression,我想用linq表达式序列化一个对象。让我们假设,我有以下课程: public class User { public string Username { get; set; } public string Password { get; set; } public IList<Usergroup> Usergroups { get; set; } } public class Usergroup { public string Name { ge
public class User
{
public string Username { get; set; }
public string Password { get; set; }
public IList<Usergroup> Usergroups { get; set; }
}
public class Usergroup
{
public string Name { get; set; }
public List<User> Users { get; set; }
public List<AccessRight> AccessRights { get; set; }
public Screen Screen { get; set; }
}
public class AccessRight
{
public int AccessLevel { get; set; }
}
public class Screen
{
public string Name { get; set; }
}
公共类用户
{
公共字符串用户名{get;set;}
公共字符串密码{get;set;}
公共IList用户组{get;set;}
}
公共类用户组
{
公共字符串名称{get;set;}
公共列表用户{get;set;}
公共列表访问权限{get;set;}
公共屏幕屏幕{get;set;}
}
公共类访问权
{
公共int访问级别{get;set;}
}
公共类屏幕
{
公共字符串名称{get;set;}
}
现在我有了一个对象用户,我想用它的用户组对它进行序列化,对于所有用户组,它的访问权限和屏幕,但不包括其他用户的列表
是否有一种方法(或可用的Git/NuGet项目)可以使用linq表达式实现这一点。
比如:
var string=Serialize(user,u=>u.Usergroups,u=>u.Usergroups.Select(ug=>ug.AccessRights),…)
当我搜索json.net和表达式时,我只找到了如何序列化表达式本身的解决方案
我想使用该解决方案初始化和取消固定NHibernate实体
提前谢谢你试过NuGet吗
假设我有以下数据:
var user = new User()
{
Username = "User 1",
Password = "***",
Usergroups = new List<Usergroup>()
{
new Usergroup()
{
Name = "Administrator",
Screen = new Screen() { Name = "Users" },
AccessRights = new List<AccessRight>()
{
new AccessRight() { AccessLevel = 9999 }
},
Users = new List<User>()
{
new User()
{
Password = "@#$%",
Usergroups = new List<Usergroup>(),
Username = "User 2"
}
}
}
}
};
//Here you serialize your object.
var json = JsonConvert.SerializeObject(user);
预期结果
{
"Username": "User 1",
"Password": "***",
"Usergroups": [{
"Name": "Administrator",
"AccessRights": [
{
"AccessLevel": 9999
}
],
"Screen": {
"Name": "Users"
}
}]
}
你试过NuGet吗
假设我有以下数据:
var user = new User()
{
Username = "User 1",
Password = "***",
Usergroups = new List<Usergroup>()
{
new Usergroup()
{
Name = "Administrator",
Screen = new Screen() { Name = "Users" },
AccessRights = new List<AccessRight>()
{
new AccessRight() { AccessLevel = 9999 }
},
Users = new List<User>()
{
new User()
{
Password = "@#$%",
Usergroups = new List<Usergroup>(),
Username = "User 2"
}
}
}
}
};
//Here you serialize your object.
var json = JsonConvert.SerializeObject(user);
预期结果
{
"Username": "User 1",
"Password": "***",
"Usergroups": [{
"Name": "Administrator",
"AccessRights": [
{
"AccessLevel": 9999
}
],
"Screen": {
"Name": "Users"
}
}]
}
这里最简单的方法是使用匿名类型,在序列化时,我们只需要定义一个与预期类型类似的结构,它不必是支持反序列化的精确克隆。
我们可以很容易地省略构造的匿名类型中的属性,然后将其序列化 我说这种方法“简单”,因为它不涉及对模型定义或DTO声明的修改。这是一个非侵入性的解决方案,可以应用于序列化场景中的所有模型,在这些场景中,您不需要或不希望序列化完整的对象图 现在让我们把nhibernate从图片中去掉,如果您有一个
User
类的实例想要序列化,您可以简单地使用它
var simpleCereal = Newtonsoft.Json.JsonConvert.SerializeObject(new { user.Username, user.Password, UserGroups = user.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
即使我们使用了匿名类型,如果指定忽略JsonSerializerSettings中缺少的成员,序列化结果仍将反序列化回有效的User
对象newJSONSerializerSettings{MissingMemberHandling=MissingMemberHandling.Ignore})代码>
我们可以通过接受lambda的简单helper方法实现您建议的语法:
/// <summary>
/// Serialize an object through a lambda expression that defines the expected output structure
/// </summary>
/// <typeparam name="T">Type of the object to serialize</typeparam>
/// <param name="target">The target object to serialize</param>
/// <param name="expr">The lambda expression that defines the final structure of the serialized object</param>
/// <returns>Serialized lambda representation of the target object</returns>
public static string Serialize<T>(T target, System.Linq.Expressions.Expression<Func<T, object>> expr)
{
var truncatedObject = expr.Compile().Invoke(target);
return Newtonsoft.Json.JsonConvert.SerializeObject(truncatedObject);
}
下面是我用来测试这个的代码,省略了OPs类定义,你可以在问题中找到它们
static void Main(string[] args)
{
User user = new User
{
Username = "Test User",
Password = "Password",
Usergroups = new List<Usergroup>
{
new Usergroup
{
Name = "Group12", AccessRights = new List<AccessRight>
{
new AccessRight { AccessLevel = 1 },
new AccessRight { AccessLevel = 2 }
},
Screen = new Screen { Name = "Home" },
Users = new List<User>
{
new User { Username = "Other1" },
new User { Username = "Other2" }
}
},
new Usergroup
{
Name = "Group3Only", AccessRights = new List<AccessRight>
{
new AccessRight { AccessLevel = 3 },
},
Screen = new Screen { Name = "Maintenance" },
Users = new List<User>
{
new User { Username = "Other1" },
new User { Username = "Other2" }
}
}
}
};
// Standard deep serialization, will include the deep User objects within the user groups.
var standardCereal = Newtonsoft.Json.JsonConvert.SerializeObject(user);
// Simple anonymous type serialize, exclude Users from within UserGroups objects
var simpleCereal = Newtonsoft.Json.JsonConvert.SerializeObject(new { user.Username, user.Password, UserGroups = user.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
// Same as above but uses a helper method that accepts a lambda expression
var lambdaCereal = Serialize(user, u => new { u.Username, u.Password, UserGroups = u.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
// NOTE: simple and lambda serialization results will be identical.
// deserialise back into a User
var userObj = Newtonsoft.Json.JsonConvert.DeserializeObject<User>(
lambdaCereal,
new Newtonsoft.Json.JsonSerializerSettings
{
MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore
});
}
/// <summary>
/// Serialize an object through a lambda expression that defines the expected output structure
/// </summary>
/// <typeparam name="T">Type of the object to serialize</typeparam>
/// <param name="target">The target object to serialize</param>
/// <param name="expr">The lambda expression that defines the final structure of the serialized object</param>
/// <returns>Serialized lambda representation of the target object</returns>
public static string Serialize<T>(T target, System.Linq.Expressions.Expression<Func<T, object>> expr)
{
var truncatedObject = expr.Compile().Invoke(target);
return Newtonsoft.Json.JsonConvert.SerializeObject(truncatedObject);
}
您提到您正在使用nhibernate,如果对象尚未在内存中具体化,那么如果您没有在UserGroups
扩展上扩展Users
属性(因此不急于加载Users
属性),那么它将不会包含在序列化输出中
这里最简单的方法是使用匿名类型,在序列化时,我们只需要定义一个与预期类型类似的结构,它不必是支持反序列化的精确克隆。
我们可以很容易地省略构造的匿名类型中的属性,然后将其序列化
我说这种方法“简单”,因为它不涉及对模型定义或DTO声明的修改。这是一个非侵入性的解决方案,可以应用于序列化场景中的所有模型,在这些场景中,您不需要或不希望序列化完整的对象图
现在让我们把nhibernate从图片中去掉,如果您有一个User
类的实例想要序列化,您可以简单地使用它
var simpleCereal = Newtonsoft.Json.JsonConvert.SerializeObject(new { user.Username, user.Password, UserGroups = user.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
即使我们使用了匿名类型,如果指定忽略JsonSerializerSettings中缺少的成员,序列化结果仍将反序列化回有效的User
对象
newJSONSerializerSettings{MissingMemberHandling=MissingMemberHandling.Ignore})代码>
我们可以通过接受lambda的简单helper方法实现您建议的语法:
/// <summary>
/// Serialize an object through a lambda expression that defines the expected output structure
/// </summary>
/// <typeparam name="T">Type of the object to serialize</typeparam>
/// <param name="target">The target object to serialize</param>
/// <param name="expr">The lambda expression that defines the final structure of the serialized object</param>
/// <returns>Serialized lambda representation of the target object</returns>
public static string Serialize<T>(T target, System.Linq.Expressions.Expression<Func<T, object>> expr)
{
var truncatedObject = expr.Compile().Invoke(target);
return Newtonsoft.Json.JsonConvert.SerializeObject(truncatedObject);
}
下面是我用来测试这个的代码,省略了OPs类定义,你可以在问题中找到它们
static void Main(string[] args)
{
User user = new User
{
Username = "Test User",
Password = "Password",
Usergroups = new List<Usergroup>
{
new Usergroup
{
Name = "Group12", AccessRights = new List<AccessRight>
{
new AccessRight { AccessLevel = 1 },
new AccessRight { AccessLevel = 2 }
},
Screen = new Screen { Name = "Home" },
Users = new List<User>
{
new User { Username = "Other1" },
new User { Username = "Other2" }
}
},
new Usergroup
{
Name = "Group3Only", AccessRights = new List<AccessRight>
{
new AccessRight { AccessLevel = 3 },
},
Screen = new Screen { Name = "Maintenance" },
Users = new List<User>
{
new User { Username = "Other1" },
new User { Username = "Other2" }
}
}
}
};
// Standard deep serialization, will include the deep User objects within the user groups.
var standardCereal = Newtonsoft.Json.JsonConvert.SerializeObject(user);
// Simple anonymous type serialize, exclude Users from within UserGroups objects
var simpleCereal = Newtonsoft.Json.JsonConvert.SerializeObject(new { user.Username, user.Password, UserGroups = user.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
// Same as above but uses a helper method that accepts a lambda expression
var lambdaCereal = Serialize(user, u => new { u.Username, u.Password, UserGroups = u.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });
// NOTE: simple and lambda serialization results will be identical.
// deserialise back into a User
var userObj = Newtonsoft.Json.JsonConvert.DeserializeObject<User>(
lambdaCereal,
new Newtonsoft.Json.JsonSerializerSettings
{
MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore
});
}
/// <summary>
/// Serialize an object through a lambda expression that defines the expected output structure
/// </summary>
/// <typeparam name="T">Type of the object to serialize</typeparam>
/// <param name="target">The target object to serialize</param>
/// <param name="expr">The lambda expression that defines the final structure of the serialized object</param>
/// <returns>Serialized lambda representation of the target object</returns>
public static string Serialize<T>(T target, System.Linq.Expressions.Expression<Func<T, object>> expr)
{
var truncatedObject = expr.Compile().Invoke(target);
return Newtonsoft.Json.JsonConvert.SerializeObject(truncatedObject);
}
您提到您正在使用nhibernate,如果对象尚未在内存中具体化,那么如果您没有在UserGroups
扩展上扩展Users
属性(因此不急于加载Users
属性),那么它将不会包含在序列化输出中
谢谢你的回答。我已经使用了Newtonsoft.Json,我还知道JsonIgnore属性。但是我想在不同的上下文中序列化用户。当我在登录的上下文中读取用户时,我需要用户及其用户组和用户组的属性(如AccessRights等)。但是当我在UserManagement的上下文中读取用户时,我只需要没有AccessRights的UserGroups。当我在UserGroupManagement的上下文中读取UserGroup时,我还需要用户。这就是我尝试动态序列化它的原因。你读过关于条件属性序列化的内容吗?那将是我的替代解决方案。实际上,我刚刚编写了一个JsonIgnoreWithContextAttribute,所以我可以决定是否忽略属性。但是这个解决方案感觉。。。嗯。。。复古。也许您还可以使用DTO来公开每个场景所需的属性,而不是将其序列化。我已经仔细考虑了很久,是否应该使用DTO,或者是否应该将实体直接传递到各个层。我还在评估阶段。实体和DTO中的重复代码阻止我使用DTO。然后我需要多个UsergroupDto。Ohe是LoginUsergroupDto,一个是UserManagementUserGroup