C# Json.Net在序列化时结合了2个属性
我对Json.Net和(反)序列化还是相当陌生的。我有一个对象,它有几个字段C# Json.Net在序列化时结合了2个属性,c#,json,serialization,json.net,C#,Json,Serialization,Json.net,我对Json.Net和(反)序列化还是相当陌生的。我有一个对象,它有几个字段 public class Person { public string Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address1 { get; set; } public string Address2 { g
public class Person {
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
}
var p = new Person() {
Id = 123,
FirstName = "John",
LastName = "Doe",
Address1 = "456 Main St",
Address2 = "Apt 2"
}
我需要序列化到以下json
{
"id": 123
, "fullName": "John Doe"
, "street": "456 Main St Apt 2"
}
并将其反序列化为
// Person.Id = 123
// Person.Address1 = 456 Main St Apt 2
// Person.Address2 = null
// we are ok not trying to split the 'street' into both 'Address1' and 'Address2'
我不确定做这件事的最佳方法。我是否需要使用转换器
、扩展数据
、构造函数
,或者其他我不知道如何搜索而找不到的东西;)
任何帮助和示例都非常有用。要组合这两个属性,可以添加如下属性:
public string FullName => FirstName + " " + LastName;
当然,您需要一些处理空值等的逻辑
您可以使用属性装饰类,这些属性将决定如何序列化属性名。你的班级最终可能会变成这样:
using Newtonsoft.Json;
using System.Linq;
public class Person {
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("fullName")]
public string FullName
{
get
{
return FirstName + " " + LastName;
}
set
{
if (value.Contains(" "))
{
var split = value.Split(' ');
FirstName = split[0];
LastName = string.Join(" ", split.Skip(1));
}
else
{
FirstName = value;
}
}
}
[JsonIgnore]
public string FirstName { get; set; }
[JsonIgnore]
public string LastName { get; set; }
}
JsonProperty属性允许您在序列化属性时设置属性的名称,而JsonIgnore属性告诉序列化程序不要在输出中包含这些属性
然后,您可以使用以下方法获取JSON:
var json = JsonConvert.SerializeObject(person);
@Alan Buchanan是一个很好的简单方法,可以实现您在问题中描述的目标。但是,如果不想向类添加其他属性,则需要使用自定义属性。下面是一个如何编写的示例:
class PersonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Person);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Person person = (Person)value;
JObject result = new JObject();
result.Add("id", person.Id);
result.Add("fullname", (person.FirstName + " " + person.LastName).Trim());
result.Add("address", (person.Address1 + " " + person.Address2).Trim());
result.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
Person person = new Person();
person.Id = (int)obj["id"]; // assuming id will always be present in the JSON
string fullName = (string)obj["fullname"];
if (fullName != null)
{
string[] parts = fullName.Split(new char[] { ' ' }, 2);
// if there's only 1 part, I'm assuming it is the first name
if (parts.Length > 0)
person.FirstName = parts[0];
if (parts.Length > 1)
person.LastName = parts[1];
}
person.Address1 = (string)obj["address"]; // don't bother trying to split address
return person;
}
}
您会注意到PersonConverter
类包含三个方法,所有这些方法都需要由它继承的JsonConverter
抽象基类实现:CanConvert
、ReadJson
和WriteJson
CanConvert
方法告诉Json.Net这个转换器处理Person
对象。根据您使用转换器的方式,Json.Net可以调用或不调用此方法。稍后将对此进行详细介绍
WriteJson
负责在序列化期间为目标对象创建JSON。您可以在这里看到,我在内部使用a来使用Person
类中的数据构建JSON。您也可以直接调用writer上的方法,而不是使用JObject
,但我发现这样做有点麻烦
ReadJson
处理相反的问题:在反序列化过程中从JSON重新构造对象。同样,我使用JObject
作为中介从读取器获取数据,然后将其拆分为新的Person
实例
如果只需要序列化而不需要反序列化,则可以重写CanRead
以返回false
,然后只需使ReadJson
抛出一个NotImplementedException
。类似地,如果只需要反序列化而不需要序列化,则可以重写CanWrite
属性
有两种方法可以使用JsonConverter
:您可以将实例传递给序列化/反序列化方法(通过方法参数或JsonSerializerSettings
)
。。。或者,您可以使用[JsonConverter]
属性将转换器绑定到您的类:
[JsonConverter(typeof(PersonConverter))]
public class Person
{
...
}
如果使用前一种方法,那么Json.Net将调用CanConvert
,以确定转换器可以处理的类型。如果使用该属性,则永远不会调用CanConvert
,因为Json.Net假定您会在属性中为目标类指定正确的转换器类型
这里是一个往返演示:虽然@Alan Buchanan's和@Brian Rogers展示了很棒的解决方案,但它们并没有以一种简单的方式实现我所需要的
对我来说最有效的方法是使用onserialized
和onserialized
。如果我做错了什么,这可能更好,或者应该做些不同的事情,请让我知道
注意:我知道在反序列化后,地址2
字段将为空,地址1
将是地址1
和地址2
的组合,但这对我们的实现来说是可以的
public class Person {
public string Id { get; set; }
[JsonProperty("fullName")]
public string FullName { get; set; }
[JsonIgnore]
public string FirstName { get; set; }
[JsonIgnore]
public string LastName { get; set; }
[JsonProperty("street")]
public string Street { get; set; }
[JsonIgnore]
public string Address1 { get; set; }
[JsonIgnore]
public string Address2 { get; set; }
[OnSerializing]
internal void OnSerializing(StreamingContext context) {
Street = (Address1 + " " + Address2).Trim();
}
[OnDeserialized]
internal void OnDeserialized(StreamingContext context) {
Address1 = Street;
}
}
// C# Object
var p = new Person() {
Id = 123,
FirstName = "John",
LastName = "Doe",
Address1 = "456 Main St",
Address2 = "Apt 2"
}
// Json to output and consume
{ "id": 123, "fullName": "John Doe", "street": "456 Main St Apt 2" }
OP明确表示他已经在使用JSON.net。不需要关于Newtonsoft的额外说明,它们是一样的Tanks Alan,我确实使用Newtonsoft.Json(我昨天了解到,大多数搜索都使用Json.Net,因为它是一样的东西)=),但是,我确实有些担心,因为我确实需要序列化/反序列化其他字段,它们遵循类似的模式,不希望以这种方式设置。我将更新我的问题以反映这一点。对不起,我倾向于简化问题,使其不那么大/复杂,但当给出答案时,我意识到我应该使其复杂化。然而,你的回答对于我的其他场景来说绝对是值得的。@RoLYroLLs没有问题。您必须告诉序列化程序如何在某个地方组合属性,因此如果不在类上,我不确定您可以在哪里进行组合。希望其他人会有一个解决方案:)谢谢。据我所见,我认为我应该使用onserialization
,但我还没有通过足够的示例来正确地理解它,或者我是否走上了正确的道路。我也希望有人能帮我指明正确的方向。谢谢!与序列化相比,这似乎是我必须采用的方法。我会测试它,让你知道它是如何工作的。Brian,让我问你,对于这个位于类
级别的转换器
,我需要添加我想要(反)序列化的所有属性,还是只添加我想要转换的属性?我在你的fiddle示例中测试了我的想法,是的,我确实需要包括该转换器上的所有字段。是否有其他方法仅操纵某些属性?我有很多属性的对象,这些属性不值得添加到这个转换器中,只有1或2个这样的字段。
public class Person {
public string Id { get; set; }
[JsonProperty("fullName")]
public string FullName { get; set; }
[JsonIgnore]
public string FirstName { get; set; }
[JsonIgnore]
public string LastName { get; set; }
[JsonProperty("street")]
public string Street { get; set; }
[JsonIgnore]
public string Address1 { get; set; }
[JsonIgnore]
public string Address2 { get; set; }
[OnSerializing]
internal void OnSerializing(StreamingContext context) {
Street = (Address1 + " " + Address2).Trim();
}
[OnDeserialized]
internal void OnDeserialized(StreamingContext context) {
Address1 = Street;
}
}
// C# Object
var p = new Person() {
Id = 123,
FirstName = "John",
LastName = "Doe",
Address1 = "456 Main St",
Address2 = "Apt 2"
}
// Json to output and consume
{ "id": 123, "fullName": "John Doe", "street": "456 Main St Apt 2" }