Как сериализовать объект по выражению Linq - PullRequest
1 голос
/ 08 июня 2019

Я хочу сериализовать объект по выражению 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 и я хочу сериализовать его со своими группами пользователей, а для всей группы пользователей - его AccessRights и Screen, но без списка других пользователей.

Есть ли способ (или пригодный для использования проект Git / NuGet) сделать это с помощью выражения linq. Как:

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

Когда я искал json.net и Expression, я нашел только решения, как сериализовать само выражение.

Я хочу использовать решение для инициализации и снятия прокси с сущностей NHibernate.

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 09 июня 2019

Самый простой способ здесь - использовать анонимные типы, при сериализации нам нужно только определить структуру, которая похожа на ожидаемый тип, это не обязательно должен быть точный клон для поддержки десериализации.
Мы можем легко опустить свойства в построенном анонимном типе и затем сериализовать это.

Я говорю, что этот метод «прост», потому что он не включает в себя модификации определения модели или объявления 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() });

Несмотря на то, что мы использовали анонимные типы, результат сериализации все равно будет десериализован обратно в действительный объект User, если вы укажете игнорировать отсутствующие элементы в JsonSerializerSettings
new JsonSerializerSettings { MissingMemberHandling = 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);
}

Ваш синтаксис теперь поддерживается:

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 (и, следовательно, не будете охотно загружать свойство Users) , то он не будет включен в вывод сериализации.

0 голосов
/ 08 июня 2019

Вы пробовали 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"
      }
     }]
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...