更改从 C# 中的 JSON 字符串反序列化的对象中的属性名称
Change Names of Properties in Object which was Deserialize from a JSON String in C#
我的反序列化对象的名称有问题,我想 return。
目前我正在编写一个 WebService,它从另一个 WebService 请求数据,然后 return将此数据发送到应用程序。
示例:
public class UserController
{
public HttpResponseMessage GetUser()
{
// Get Data from WebService
string jsonString = @"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', address{'field3': 'HelloWorld Ave.', 'field4': 'Somewhere'}}";
// Make Object from JSON-String
User user = JsonConvert.DeserializeObject<User>(jsonString);
// Return Object to Application
return Request.CreateResponse(HttpStatusCode.OK, user);
}
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("field1")]
public string FirstName { get; set; }
[JsonProperty("field2")]
public string LastName { get; set; }
[JsonProperty("address")]
public Address Address { get; set; }
}
public class Address
{
[JsonProperty("field3")]
public string Street { get; set; }
[JsonProperty("field4")]
public string City { get; set; }
}
到目前为止一切顺利。我的 WebService 创建对象 "User" 并将其 return 发送到应用程序。
现在我的问题是:
returning JSON 字符串将字段名称更改回其原始名称。
而不是:
"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
我得到:
"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
我知道 属性Json属性 中的名称不区分大小写,所以我可以将 [JsonProperty("Key")]
大写 "K" 并returning Json-String 将包含大写 "K".
但是我的字段呢?有什么方法可以将 "field1" 更改为 "FirstName",将 "field2" 更改为 "LastName"?
编辑 1 - 2015-01-28:向示例添加了更多代码。
通过编写您自己的序列化程序并向属性添加自定义属性,您可以强制序列化程序在序列化回 Json 时采用自定义名称。
public class JsonSerializeProperty : Attribute
{
public string PropertyName { get; set; }
}
// derive from JsonConverter and override WriteJson (serialize)
// and ReadJson (deserialize)
// note: i have not implemented CanConvert
public class CustomUserSerializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var user = value as User;
if(user == null)
throw new NullReferenceException("user");
var properties = user.GetType().GetProperties();
writer.WriteStartObject();
foreach (var property in properties)
{
// get the attribute assigned to the property [JsonSerializeProperty]
object customAttributes = property.GetCustomAttributes(typeof(JsonSerializeProperty), false).SingleOrDefault();
JsonSerializeProperty attribute = customAttributes as JsonSerializeProperty;
if(attribute != null)
{
// JsonSerializeProperty
string propertyName = attribute.PropertyName;
// just write new property name and value
writer.WritePropertyName(propertyName);
writer.WriteValue(property.GetValue(value, new object[] {}));
}
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// just map every JProperty from the Json string to the
// JsonProperty of the user class. I know this is kind of ugly... but it may serve your purpose
JObject jsonObject = JObject.Load(reader);
List<JProperty> properties = jsonObject.Properties().ToList();
// create an instance of User to assign the values of the
// Json string
object instance = Activator.CreateInstance(objectType);
// loop through the user properties and get the
// JsonProperty customattribute. then set the value of the JProperty
PropertyInfo[] objectProperties = objectType.GetProperties();
foreach (var objectProperty in objectProperties)
{
var customAttribute = objectProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), false).SingleOrDefault();
JsonPropertyAttribute attribute = customAttribute as JsonPropertyAttribute;
if (attribute != null)
{
JProperty jsonProperty = properties.SingleOrDefault(prop => prop.Name == attribute.PropertyName);
if (jsonProperty != null)
{
objectProperty.SetValue(instance, jsonProperty.Value.ToString(), new object[] {});
}
}
}
return instance;
// {
// _Key = "A45",
// _FirstName = "John",
// _LastName = "Doe"
// }
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
[JsonConverter(typeof(CustomUserSerializer))]
public class User
{
[JsonProperty(PropertyName = "key")]
[JsonSerializeProperty(PropertyName = "_Key")]
public string Key { get; set; }
[JsonProperty("field1")]
[JsonSerializeProperty(PropertyName = "_FirstName")]
public string FirstName { get; set; }
[JsonProperty("field2")]
[JsonSerializeProperty(PropertyName = "_LastName")]
public string LastName { get; set; }
}
我建议执行 2 个步骤
- 您按原样(反)序列化您的 DTO(数据传输对象)
- 您实现了一个装配层,您可以在其中控制域对象的创建。
在你的情况下,它似乎是 DTO 和 DomainModel 之间的 1:1 关系,只是 属性 名称不同 - 所以你只需要那样投影。为此,我强烈推荐 Automapper Projections or in case it's nested Automapper Nested Projections。
这里的最大优势是您将整个域层与外部服务分离,因此即使您的网络服务出现故障/更改,也不会影响您的业务逻辑。
@stefankmitph 和@nhaberl 的回答都很好,但我最后使用了稍微不同的技术。
我的两个 classes 现在都有私有字段,用于将 JSON-String 的值设置为 public-fields。
私有字段获取传入 JSON-String 的 PropertyName,public-字段获取传出 JSON-String:
的 PropertyName
public class User
{
#region private
[JsonProperty("key")]
private string _Key { set { Key = value; } }
[JsonProperty("field1")]
private string _FirstName { set { FirstName = value; } }
[JsonProperty("field2")]
private string _LastName { set { LastName = value; } }
[JsonProperty("address")]
private Address _Address { set { Address = value; } }
#endregion
#region public
[JsonProperty("Key")]
public string Key { get; private set; }
[JsonProperty("FirstName")]
public string FirstName { get; private set; }
[JsonProperty("LastName")]
public string LastName { get; private set; }
[JsonProperty("Address")]
public Address Address { get; private set; }
#endregion
}
public class Address
{
#region private
[JsonProperty("field3")]
private string _Street { set { Key = value; } }
[JsonProperty("field4")]
private string _City { set { FirstName = value; } }
#endregion
#region public
[JsonProperty("Street")]
public string Street { get; private set; }
[JsonProperty("City")]
public string City { get; private set; }
#endregion
}
示例输出:
"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
通过这种方法,我还可以从底层 classes 获取所有数据到主 class:
public class User
{
#region private
// Other fields, see above. \
[JsonProperty("address")]
private Address _Address
{
set
{
City = value.City;
Street = value.Street;
}
}
#endregion
#region public
// Other fields, see above. \
[JsonProperty("City")]
public string City { get; private set; }
[JsonProperty("Street")]
public string Street { get; private set; }
#endregion
}
public class Address
{
// Address fields. See above.
}
示例输出:
"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', 'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}"
我的反序列化对象的名称有问题,我想 return。
目前我正在编写一个 WebService,它从另一个 WebService 请求数据,然后 return将此数据发送到应用程序。
示例:
public class UserController
{
public HttpResponseMessage GetUser()
{
// Get Data from WebService
string jsonString = @"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', address{'field3': 'HelloWorld Ave.', 'field4': 'Somewhere'}}";
// Make Object from JSON-String
User user = JsonConvert.DeserializeObject<User>(jsonString);
// Return Object to Application
return Request.CreateResponse(HttpStatusCode.OK, user);
}
}
public class User
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("field1")]
public string FirstName { get; set; }
[JsonProperty("field2")]
public string LastName { get; set; }
[JsonProperty("address")]
public Address Address { get; set; }
}
public class Address
{
[JsonProperty("field3")]
public string Street { get; set; }
[JsonProperty("field4")]
public string City { get; set; }
}
到目前为止一切顺利。我的 WebService 创建对象 "User" 并将其 return 发送到应用程序。
现在我的问题是:
returning JSON 字符串将字段名称更改回其原始名称。
而不是:
"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
我得到:
"{'key': 'A45', 'field1': 'John', 'field2': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
我知道 属性Json属性 中的名称不区分大小写,所以我可以将 [JsonProperty("Key")]
大写 "K" 并returning Json-String 将包含大写 "K".
但是我的字段呢?有什么方法可以将 "field1" 更改为 "FirstName",将 "field2" 更改为 "LastName"?
编辑 1 - 2015-01-28:向示例添加了更多代码。
通过编写您自己的序列化程序并向属性添加自定义属性,您可以强制序列化程序在序列化回 Json 时采用自定义名称。
public class JsonSerializeProperty : Attribute
{
public string PropertyName { get; set; }
}
// derive from JsonConverter and override WriteJson (serialize)
// and ReadJson (deserialize)
// note: i have not implemented CanConvert
public class CustomUserSerializer : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var user = value as User;
if(user == null)
throw new NullReferenceException("user");
var properties = user.GetType().GetProperties();
writer.WriteStartObject();
foreach (var property in properties)
{
// get the attribute assigned to the property [JsonSerializeProperty]
object customAttributes = property.GetCustomAttributes(typeof(JsonSerializeProperty), false).SingleOrDefault();
JsonSerializeProperty attribute = customAttributes as JsonSerializeProperty;
if(attribute != null)
{
// JsonSerializeProperty
string propertyName = attribute.PropertyName;
// just write new property name and value
writer.WritePropertyName(propertyName);
writer.WriteValue(property.GetValue(value, new object[] {}));
}
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// just map every JProperty from the Json string to the
// JsonProperty of the user class. I know this is kind of ugly... but it may serve your purpose
JObject jsonObject = JObject.Load(reader);
List<JProperty> properties = jsonObject.Properties().ToList();
// create an instance of User to assign the values of the
// Json string
object instance = Activator.CreateInstance(objectType);
// loop through the user properties and get the
// JsonProperty customattribute. then set the value of the JProperty
PropertyInfo[] objectProperties = objectType.GetProperties();
foreach (var objectProperty in objectProperties)
{
var customAttribute = objectProperty.GetCustomAttributes(typeof(JsonPropertyAttribute), false).SingleOrDefault();
JsonPropertyAttribute attribute = customAttribute as JsonPropertyAttribute;
if (attribute != null)
{
JProperty jsonProperty = properties.SingleOrDefault(prop => prop.Name == attribute.PropertyName);
if (jsonProperty != null)
{
objectProperty.SetValue(instance, jsonProperty.Value.ToString(), new object[] {});
}
}
}
return instance;
// {
// _Key = "A45",
// _FirstName = "John",
// _LastName = "Doe"
// }
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
[JsonConverter(typeof(CustomUserSerializer))]
public class User
{
[JsonProperty(PropertyName = "key")]
[JsonSerializeProperty(PropertyName = "_Key")]
public string Key { get; set; }
[JsonProperty("field1")]
[JsonSerializeProperty(PropertyName = "_FirstName")]
public string FirstName { get; set; }
[JsonProperty("field2")]
[JsonSerializeProperty(PropertyName = "_LastName")]
public string LastName { get; set; }
}
我建议执行 2 个步骤
- 您按原样(反)序列化您的 DTO(数据传输对象)
- 您实现了一个装配层,您可以在其中控制域对象的创建。
在你的情况下,它似乎是 DTO 和 DomainModel 之间的 1:1 关系,只是 属性 名称不同 - 所以你只需要那样投影。为此,我强烈推荐 Automapper Projections or in case it's nested Automapper Nested Projections。
这里的最大优势是您将整个域层与外部服务分离,因此即使您的网络服务出现故障/更改,也不会影响您的业务逻辑。
@stefankmitph 和@nhaberl 的回答都很好,但我最后使用了稍微不同的技术。
我的两个 classes 现在都有私有字段,用于将 JSON-String 的值设置为 public-fields。
私有字段获取传入 JSON-String 的 PropertyName,public-字段获取传出 JSON-String:
的 PropertyNamepublic class User
{
#region private
[JsonProperty("key")]
private string _Key { set { Key = value; } }
[JsonProperty("field1")]
private string _FirstName { set { FirstName = value; } }
[JsonProperty("field2")]
private string _LastName { set { LastName = value; } }
[JsonProperty("address")]
private Address _Address { set { Address = value; } }
#endregion
#region public
[JsonProperty("Key")]
public string Key { get; private set; }
[JsonProperty("FirstName")]
public string FirstName { get; private set; }
[JsonProperty("LastName")]
public string LastName { get; private set; }
[JsonProperty("Address")]
public Address Address { get; private set; }
#endregion
}
public class Address
{
#region private
[JsonProperty("field3")]
private string _Street { set { Key = value; } }
[JsonProperty("field4")]
private string _City { set { FirstName = value; } }
#endregion
#region public
[JsonProperty("Street")]
public string Street { get; private set; }
[JsonProperty("City")]
public string City { get; private set; }
#endregion
}
示例输出:
"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', Address {'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}}"
通过这种方法,我还可以从底层 classes 获取所有数据到主 class:
public class User
{
#region private
// Other fields, see above. \
[JsonProperty("address")]
private Address _Address
{
set
{
City = value.City;
Street = value.Street;
}
}
#endregion
#region public
// Other fields, see above. \
[JsonProperty("City")]
public string City { get; private set; }
[JsonProperty("Street")]
public string Street { get; private set; }
#endregion
}
public class Address
{
// Address fields. See above.
}
示例输出:
"{'Key': 'A45', 'FirstName': 'John', 'LastName': 'Doe', 'Street': 'HelloWorld Ave.', 'City': 'Somewhere'}"