C# Json.Net在序列化时结合了2个属性

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

我对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 { 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" }