使用2个非默认构造函数反序列化类

7

我有一个类,使用Json.net尝试对给定的对象进行序列化和反序列化。

public class JsonSerializer
{
    public string Serialize(object toSerialaze)
    {
        return JsonConvert.SerializeObject(toSerialaze);
    }

    public T Deserialize<T>(string toDeserialaze)
    {
        return JsonConvert.DeserializeObject<T>(toDeserialaze);
    }
}

给它一个属于这个类的对象

public class Isbn
{
    private readonly int _groupCode;
    private readonly int _publisherCode;
    private readonly int _titleCode;
    private readonly int _checkCode;
    private static readonly Regex Regex = new Regex(@"^\s*\d*\s*-\s*\d*\s*-\s*\d*\s*-\s*\d*\s*$");

    public Isbn(int groupCode, int publisherCode, int titleCode, int checkCode)
    {
        _groupCode = groupCode;
        _publisherCode = publisherCode;
        _titleCode = titleCode;
        _checkCode = checkCode;
    }

    public Isbn(string isbn)
    {
        if (isbn == null)
            throw new ArgumentNullException("isbn");
        if (isbn == "") return;
        if (!IsValid(isbn)) return;
        var isbnStrings = isbn.Split(new[] {'-', ' '}, StringSplitOptions.RemoveEmptyEntries);
        _groupCode = Convert.ToInt32(isbnStrings[0]);
        _publisherCode = Convert.ToInt32(isbnStrings[1]);
        _titleCode = Convert.ToInt32(isbnStrings[2]);
        _checkCode = Convert.ToInt32(isbnStrings[3]);
    }
}

我遇到了以下异常:
```Additional information: 无法找到类型 Library.Isbn 的构造函数。一个类应该有默认构造函数、一个带参数的构造函数或者一个标记有 JsonConstructor 特性的构造函数。```
我知道,我可以在反序列化时在需要使用的构造函数前面加上 ```[JsonConstructor]``` 标记,但是我不想让类 ```Isbn``` 知道 Json 的存在。有没有其他方法可以达到相同的效果呢?我应该如何让 JsonConverter 知道要使用其中的两个构造函数中的哪一个呢?

2个回答

6
我想这个答案有点晚了,但是有人可能会用到它。
您可以通过创建自定义的JsonConverter来实现。
public class IsbnConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Isbn);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            var dict = new Dictionary<string, int>();
            serializer.Populate(reader, dict);
            return new Isbn(dict["groupCode"], dict["publisherCode"], dict["titleCode"], dict["checkCode"]);
        }

        if (reader.TokenType == JsonToken.String)
        {
            return new Isbn((string)reader.Value);
        }

        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

你只需要将这个转换器传递给JsonConvert.DeserializeObject即可。
var yourobj = JsonConvert.DeserializeObject<T>(json, new IsbnConverter());

现在反序列化可以同时适用于 JSON 的两种格式。
{ .... , isbn:{groupCode:1,publisherCode:2,titleCode:3,checkCode:4}, ...... }
{ .... , isbn:"1-2-3-4", .... }

例如:

public class Book
{
    public string Title { get; set; }
    public Isbn isbn { get; set; }
}

string json1 = @"{Title:""Title 1"", isbn:{groupCode:1,publisherCode:2,titleCode:3,checkCode:4}}";
string json2 = @"{Title:""Title 2"", isbn:""1-2-3-4""}";

var book1 = JsonConvert.DeserializeObject<Book>(json1, new IsbnConverter());
var book2 = JsonConvert.DeserializeObject<Book>(json2, new IsbnConverter());

1
我觉得这个答案有点晚了,对吧?是在一个小时问完后回复的? - weston

5

一种选择是为序列化专门创建一个不同的类。然后手动或使用AutoMapper映射到您的原始类。

作为奖励,您将发现您的业务对象可以自由地进行重构,而不必担心这对序列化有什么影响。因为你是对的,它不应该知道json。

使用静态方法替换公共构造函数

另一种选择是减少构造函数的数量,我尽量不超过一个,通常没有任何(我指的是没有公共构造函数)。

示例:

public Isbn(int groupCode, int publisherCode, int titleCode, int checkCode)
{
    _groupCode = groupCode;
    _publisherCode = publisherCode;
    _titleCode = titleCode;
    _checkCode = checkCode;
}

public static Isbn FromString(string isbn)
{
    if (isbn == null)
        throw new ArgumentNullException("isbn");
    if (isbn == "") return;
    if (!IsValid(isbn)) return;
    var isbnStrings = isbn.Split(new[] {'-', ' '}, StringSplitOptions.RemoveEmptyEntries);
    var groupCode = Convert.ToInt32(isbnStrings[0]);
    var publisherCode = Convert.ToInt32(isbnStrings[1]);
    var titleCode = Convert.ToInt32(isbnStrings[2]);
    var checkCode = Convert.ToInt32(isbnStrings[3]);
    return new Isbn(groupCode, publisherCode, titleCode, checkCode);
}

Weston,感谢您的回答!不要有超过1个构造函数是一个非常好的想法! - Sergey Z.
不用谢,即使您想接受其他答案,也可以点击上箭头投票支持该回答。 - weston
如果你喜欢这个,可以查看https://dev59.com/wHVC5IYBdhLWcg3wvT_g获取更多支持该方法的论据,这基本上来自一本名为《Effective Java》的书,其中大部分内容同样适用于C#。 - weston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接