更改从 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 个步骤

  1. 您按原样(反)序列化您的 DTO(数据传输对象)
  2. 您实现了一个装配层,您可以在其中控制域对象的创建。

在你的情况下,它似乎是 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'}"