Json.Net在序列化上组合2个属性

时间:2017-08-04 14:50:50

标签: c# json serialization json.net

我仍然是Json.Net和(de)序列化的新手。我有一个有几个字段的对象

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'

我不确定这样做的最佳方法。我是否需要使用ConverterExtensionDataConstructor或其他我找不到的内容,因为我不知道如何搜索它;)< / p>

任何帮助和示例都会很棒。

3 个答案:

答案 0 :(得分:3)

要合并这两个属性,您可以添加如下所示的属性:

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);

答案 1 :(得分:2)

@Alan Buchanan shows一个很好的简单方法来实现你在问题中所描述的内容。但是,如果您不想在课程中添加其他属性,则需要使用自定义JsonConverter。以下是如何编写它的示例:

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,{{1} }和ReadJson

WriteJson方法告诉Json.Net这个转换器处理CanConvert个对象。 Json.Net可能会调用此方法,也可能不会调用此方法,具体取决于您使用转换器的方式。稍后会详细介绍。

Person负责在序列化期间为目标对象创建JSON。您可以在此处看到我在内部使用JObject来使用WriteJson类中的数据构建JSON。您也可以直接在编写器上调用方法而不是使用Person,但我发现它有点麻烦。

JObject处理反向:在反序列化期间从JSON重构对象。同样,我使用ReadJson作为中介来从读者获取数据,然后再将其拆分为新的JObject实例。

如果您只需要序列化而不是反序列化,则可以覆盖Person以返回CanRead,然后只需使false抛出ReadJson。同样,如果您只需要反序列化但不需要序列化,则可以覆盖NotImplementedException属性。

有两种方法可以使用CanWrite:您可以将实例传递给serialize / deserialize方法(通过方法参数或JsonConverter)...

JsonSerializerSettings

...或者您可以使用string json = JsonConvert.SerializeObject(p, new PersonConverter()); 属性将转换器绑定到您的班级:

[JsonConverter]

如果使用前一种方法,则Json.Net会调用[JsonConverter(typeof(PersonConverter))] public class Person { ... } 来确定转换器可以处理的类型。如果使用该属性,则永远不会调用CanConvert,因为Json.Net假定您已在目标类的属性中指定了正确的转换器类型。

这是一个往返演示:https://dotnetfiddle.net/EGok23

答案 2 :(得分:0)

虽然@Alan Buchanan的answer和@Brian Rogers answer显示了很好的解决方案,但这些并没有以简单的方式实现我所需要的。

对我来说最有效的方法是使用OnSerializingOnDeserialized。如果我做错了,可以这么好,或者应该做些不同的事情,请告诉我。

注意:我了解在Deserialization后,字段Address2将为空,Address1将是Address1Address2的组合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" } 曾经是,但这对我们的实施来说还可以。

cannot be resolved to absolute file path because it does not reside in the file system