我仍然是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'
我不确定这样做的最佳方法。我是否需要使用Converter
,ExtensionData
,Constructor
或其他我找不到的内容,因为我不知道如何搜索它;)< / p>
任何帮助和示例都会很棒。
答案 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显示了很好的解决方案,但这些并没有以简单的方式实现我所需要的。
对我来说最有效的方法是使用OnSerializing
和OnDeserialized
。如果我做错了,可以这么好,或者应该做些不同的事情,请告诉我。
注意:我了解在Deserialization
后,字段Address2
将为空,Address1
将是Address1
和Address2
的组合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