如何通过Linq Expression序列化对象

时间:2019-06-07 23:46:06

标签: 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 { 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; }
}

现在我有一个对象User,想要对其用户组进行序列化,对于所有用户组,对其访问权限和屏幕进行序列化,但没有其他用户列表。

是否存在使用linq表达式执行此操作的方法(或可用的Git / NuGet项目)。 喜欢:

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

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

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

预先感谢

2 个答案:

答案 0 :(得分:0)

您尝试过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);

对于对象 Usergroup 中的属性 Users ,我放置了属性[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"
      }
     }]
 }

答案 1 :(得分:0)

这里最简单的方法是使用匿名类型,当序列化时,我们只需要定义一个与预期类型相似的结构即可,不必为支持反序列化而精确克隆。
我们可以轻松地忽略构造的匿名类型中的属性,然后对其进行序列化。

  

我说这个方法很“简单”,因为它不涉及对模型定义或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,如果该对象尚未在内存中实现,那么如果您没有在Users扩展上扩展UserGroups属性(因此不急于加载{ {1}}属性),那么它将不会包含在序列化输出中。