在Web.API控制器中自动反序列化为类似字符串的类 [英] Automatically deserialize to string-like class in Web.API controller

查看:217
本文介绍了在Web.API控制器中自动反序列化为类似字符串的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Web.API endpoint that takes an object like this as a parameter:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public UserName UserName { get; set; }
}

例如:

[Route("api/person")]
[AcceptVerbs("POST")]
public void UpdatePerson(Person person)
{
    // etc.
}

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

我们的UserName类是一个对象,它为string定义了隐式运算符,因此在整个应用程序中,我们将其与string一样对待.

Our UserName class is an object that defines implicit operators to string, so we treat it exactly as we would a string throughout our application.

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

Unfortunately, Web.API doesn't automatically know how to deserialize a corresponding JavaScript Person object into a C# Person object - the deserialized C# Person object is always null. For example, here's how I might call this endpoint from my JavaScript frontend, using jQuery:

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

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

If I leave off the UserName property, the data parameter is correctly deserialized into a C# Person object (with the UserName property set to null).

如何使Web.API正确反序列化JavaScript对象上的UserName属性到我们的自定义UserName类中?

How can I make Web.API properly deserialize the UserName property on the JavaScript object into our custom UserName class?

这是我的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();
    }
}

推荐答案

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

You need to write a custom Json.NET Converter for your UserName class. After you create the custom converter then you need to tell Json.NET about it. In one of my projects we added the following lines of code to the Application_Start method in your Global.asax.cs file to let Json.NET know about the converter:

// 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;
};


这里快速且基本地实施了应该有效的(未尝试的)解决方案.几乎可以肯定,它可以在以下方面得到改进:


Here is quick and basic implementation of one that should work (untested). It almost certainly could be improved upon:

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

这篇关于在Web.API控制器中自动反序列化为类似字符串的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆