在Web.API控制器中自动反序列化为类似字符串的类

Nat*_*end 10 c# json deserialization asp.net-web-api asp.net-web-api2

我有一个Web.API端点,它将这样的对象作为参数:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public UserName UserName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

例如:

[Route("api/person")]
[AcceptVerbs("POST")]
public void UpdatePerson(Person person)
{
    // etc.
}
Run Code Online (Sandbox Code Playgroud)

(这只是一个例子 - 我们实际上并没有通过我们的Web.API端点接受用户名)

我们的UserName类是一个定义隐式运算符的对象string,因此我们对string整个应用程序的处理方式完全一样.

遗憾的是,Web.API不会自动知道如何将相应的JavaScript Person对象反序列化为C#Person对象 - 反序列化的C#Person对象始终为null.例如,以下是我如何使用jQuery从我的JavaScript前端调用此端点:

$.ajax({
    type: 'POST',
    url: 'api/test',
    data: { FirstName: 'First', LastName: 'Last', Age: 110, UserName: 'UserName' }
});
Run Code Online (Sandbox Code Playgroud)

如果我不使用UserName属性,则将data参数正确反序列化为C#Person对象(UserName属性设置为null).

如何让Web.API正确地将UserNameJavaScript对象上的属性反序列化为我们的自定义UserName类?


这是我UserName班级的样子:

public class UserName
{
    private readonly string value;
    public UserName(string value)
    {
        this.value = value;
    }
    public static implicit operator string (UserName d)
    {
        return d != null ? d.ToString() : null;
    }
    public static implicit operator UserName(string d)
    {
        return new UserName(d);
    }
    public override string ToString()
    {
        return value != null ? value.ToUpper().ToString() : null;
    }

    public static bool operator ==(UserName a, UserName b)
    {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b))
            return true;

        // If one is null, but not both, return false.
        if (((object)a == null) || ((object)b == null))
            return false;

        return a.Equals(b);
    }

    public static bool operator !=(UserName a, UserName b)
    {
        return !(a == b);
    }

    public override bool Equals(object obj)
    {
        if ((obj as UserName) == null)
            return false;
        return string.Equals(this, (UserName)obj);
    }

    public override int GetHashCode()
    {
        string stringValue = this.ToString();
        return stringValue != null ? stringValue.GetHashCode() : base.GetHashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*rik 4

您需要为您的类编写一个自定义Json.NET 转换器UserName。创建自定义转换器后,您需要将其告知 Json.NET。在我的一个项目中,我们将以下代码行添加到文件Application_Start中的方法中Global.asax.cs,以使 Json.NET 了解转换器:

// Global Json.Net config settings.
JsonConvert.DefaultSettings = () =>
{
    var settings = new JsonSerializerSettings();
    // replace UserNameConverter with whatever the name is for your converter below
    settings.Converters.Add(new UserNameConverter()); 
    return settings;
};
Run Code Online (Sandbox Code Playgroud)

这是一个应该有效的快速且基本的实现(未经测试)。几乎可以肯定它可以改进:

public class UserNameConverter : JsonConverter
{

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var username = (UserName)value;

        writer.WriteStartObject();
        writer.WritePropertyName("UserName");
        serializer.Serialize(writer, username.ToString());
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Variables to be set along with sensing variables
        string username = null;
        var gotName = false;

        // Read the properties
        while (reader.Read())
        {
            if (reader.TokenType != JsonToken.PropertyName)
            {
                break;
            }

            var propertyName = (string)reader.Value;
            if (!reader.Read())
            {
                continue;
            }

            // Set the group
            if (propertyName.Equals("UserName", StringComparison.OrdinalIgnoreCase))
            {
                username = serializer.Deserialize<string>(reader);
                gotName = true;
            }
        }

        if (!gotName)
        {
            throw new InvalidDataException("A username must be present.");
        }

        return new UserName(username);
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(UserName);
    }
}
Run Code Online (Sandbox Code Playgroud)