如何通过 Linq 表达式序列化对象



我想通过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 { 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; }
}

现在我有一个对象用户,并希望使用它的用户组对其进行序列化,对于所有用户组,它的访问权限和屏幕,但没有其他用户列表。

有没有办法(或可用的 Git/NuGet 项目)使用 linq 表达式执行此操作。 喜欢:

var string = Serialize<User>(user, u => u.Usergroups, u => u.Usergroups.Select(ug => ug.AccessRights), ...)

当我搜索 json.net 和表达式时,我只找到了如何序列化表达式本身的解决方案。

我想使用该解决方案来初始化和取消代理 NHibernate 实体。

提前致谢

你试过 Json.NET 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);

对于对象用户组中的用户属性,我输入属性:[JsonIgnore]

public class Usergroup
{
public string Name { get; set; }
[JsonIgnore]
public List<User> Users { get; set; }
public List<AccessRight> AccessRights { get; set; }
public Screen Screen { get; set; }
}

预期成果

{
"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对象new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Ignore });

我们可以通过接受 lambda 的简单帮助程序方法实现您建议的语法:

/// <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);
}

现在支持您的语法:

string lambdaCereal = Serialize(user, u => new { u.Username, u.Password, UserGroups = u.Usergroups.Select(ug => new { ug.AccessRights, ug.Name, ug.Screen }).ToList() });

以下是我用来测试的代码,省略了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);
}

在上面的代码中simpleCereal == lambdaCereal=>true

lambdaCereal 的结果是:

{
"Username": "Test User",
"Password": "Password",
"UserGroups": [
{
"AccessRights": [
{
"AccessLevel": 1
},
{
"AccessLevel": 2
}
],
"Name": "Group12",
"Screen": {
"Name": "Home"
}
},
{
"AccessRights": [
{
"AccessLevel": 3
}
],
"Name": "Group3Only",
"Screen": {
"Name": "Maintenance"
}
}
]
}

提到您正在使用 nhibernate,如果对象尚未在内存中具体化,那么如果您没有在UserGroups扩展时展开Users属性(因此没有急切地加载Users属性),那么它将不会包含在序列化输出中。

最新更新