Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm working on a project with SignalR, and I've got some objects that I'm going to be passing along through it. These objects are only explicitly created in my back-end code, and I'd really like to be able to enforce immutability and invariants on them. I'm running into the issue that SignalR requires me (well, really NewtonSoft.Json), to have a default, no-args constructor and public setters on my properties in order for it to serialize and deserialize them over the wire.

Here's a contrived example:

public class Message{
    public string Text {get;set;}
    public int Awesomeness {get;set;}
}

What I'd like, is something more along these lines (it should probably have readonly private fields and getter-only properties to be fully immutable, but for something that is just a POCO with no methods, good enough)

public class Message {
    public string Text {get;private set;}
    public int Awesomeness {get;private set;}
    public Message( string msg, int awesome){
        if (awesome < 1 || awesome > 5){
            throw new ArgumentOutOfRangeException("awesome");
        }
        Text = msg;
        Awesomeness = awesome;
    }
}

If I do that, though, my object can't get deserialized by the SignalR .NET client library. I can just chuck a default constructor in there, and make my setters public, but then I have to remember to not use them in my code, and make sure no one else on the team uses them without understanding.

I've kicked around the idea of doing this, to mark the default constructor as something that should never be explicitly used:

[Obsolete("Bad! don't do this!")
public Message(){}

But I can't use the Obsolete attribute on just the setter of a property.

If I really wanted to, I could separate out the "real" object from a DTO representation and convert between them, but I'm not really psyched up to write a bunch of boilerplate to do that and introduce another layer.

Is there something I'm overlooking, or do I just need to bite the bullet and deal with it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
134 views
Welcome To Ask or Share your Answers For Others

1 Answer

If your class does not have a public parameterless constructor, but does have a single public constructor with parameters, Json.NET will call that constructor, matching the constructor arguments to the JSON properties by name using reflection and using default values for missing properties. Matching by name is case-insensitive, unless there are multiple matches that differ only in case, in which case the match becomes case sensitive. Thus if you simply do:

public class Message
{
    public string Text { get; private set; }
    public int Awesomeness { get; private set; }
    public Message(string text, int awesomeness)
    {
        if (awesomeness < 1 || awesomeness > 5)
        {
            throw new ArgumentOutOfRangeException("awesome");
        }
        this.Text = text;
        this.Awesomeness = awesomeness;
    }
}

You will be able to serialize and deserialize your class successfully with Json.NET.

Prototype fiddle.

If your class has multiple public constructors, all with parameters, you can mark the one to use with [JsonConstructor], e.g.:

public class Message
{
    public string Text { get; private set; }
    public int Awesomeness { get; private set; }

    public Message(string Text)
        : this(Text, 1)
    {
    }

    [JsonConstructor]
    public Message(string text, int awesomeness)
    {
        if (awesomeness < 1 || awesomeness > 5)
        {
            throw new ArgumentOutOfRangeException("awesome");
        }
        this.Text = text;
        this.Awesomeness = awesomeness;
    }
}

See also JsonSerializerSettings.ConstructorHandling which tells Json.NET whether to prefer a non-public parameterless constructor over a single public constructor with parameters.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...