Самый простой способ здесь - использовать анонимные типы, при сериализации нам нужно только определить структуру, которая похожа на ожидаемый тип, это не обязательно должен быть точный клон для поддержки десериализации.
Мы можем легко опустить свойства в построенном анонимном типе и затем сериализовать это.
Я говорю, что этот метод «прост», потому что он не включает в себя модификации определения модели или объявления 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
) , то он не будет включен в вывод сериализации.