RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.77k stars 1.29k forks source link

newtonsoft.json optimizations and deserialization #10

Closed kervi closed 8 years ago

kervi commented 8 years ago

My Web api (WebApi2) service returns json without default values. If for instance an int value is 0 or a bool is false it is not included in the json string returned.

In the response classes that NSwag returns some values have Required = Required.Always, but that gives troubles with the missing values I think it should be Required=Required.Default unless the original response classes have required values.

RicoSuter commented 8 years ago

Can you post a sample DTO class?

kervi commented 8 years ago

public partial class CreateUserResponse : INotifyPropertyChanged { private CreateUserStatus _status; private long _id;

    [JsonProperty("status", Required = Required.Always)]
    public CreateUserStatus Status
    {
        get { return _status; }
        set 
        {
            if (_status != value)
            {
                _status = value; 
                RaisePropertyChanged();
            }
        }
    }

    [JsonProperty("Id", Required = Required.Always)]
    public long Id
    {
        get { return _id; }
        set 
        {
            if (_id != value)
            {
                _id = value; 
                RaisePropertyChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public string ToJson() 
    {
        return JsonConvert.SerializeObject(this);
    }

    public static CreateUserResponse FromJson(string data)
    {
        return JsonConvert.DeserializeObject<CreateUserResponse>(data);
    }

    protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) 
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public enum CreateUserStatus
{
    Success, 
    InvalidUserName, 
    InvalidPassword, 
    InvalidQuestion, 
    InvalidAnswer, 
    InvalidEmail, 
    DuplicateUserName, 
    DuplicateEmail, 
    UserRejected, 
    InvalidProviderUserKey, 
    DuplicateProviderUserKey, 
    ProviderError, 
}
kervi commented 8 years ago

Class in web api

public class CreateUserResponse { public enum CreateUserStatus { Success = 0, InvalidUserName = 1, InvalidPassword = 2, InvalidQuestion = 3, InvalidAnswer = 4, InvalidEmail = 5, DuplicateUserName = 6, DuplicateEmail = 7, UserRejected = 8, InvalidProviderUserKey = 9, DuplicateProviderUserKey = 10, ProviderError = 11 } public CreateUserStatus status { get; set; } public int Id {get; set; } }

RicoSuter commented 8 years ago

The point here is that Id and status are required (they are not nullable)...

kervi commented 8 years ago

I know, but in the serialization to json, WebApi/jsonsoft.json does not include values that have default values. It is a design choice to minimize bandwidth.

If i create a class new CreateUserResponse(){id=0, status=CreateUserStatus.InvalidUserName};

the json returned from the webapi method is {"status":"InvalidUserName"} notice the missing id field.

To get this back correctly without exception you need to decorate with [JsonProperty("id", Required = Required.Default)]

In general you need to use Required.Default as default unless a field is marked specifically as required in the original response class.

RicoSuter commented 8 years ago

I understand, the problem with the whole generator is, that it first converts the class to JSON Schema (used in Swagger) and than back to code... A property which is not nullable (e.g. an int or enum) is set to required in the JSON Schema... When generating CSharp code from this schema, there is no way to know whether to set Default or Always depending on the required property in JSON schema...

kervi commented 8 years ago

If you are able to figure out a solution it would be best as this i important on web services with many requests. There is a workout and that is to use the following code in your web service:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;

RicoSuter commented 8 years ago

I probably have to add this as option, as in my opinion - at least in public APIs - omitting default values is bad design...

RicoSuter commented 8 years ago

Added RequiredPropertiesMustBeDefined in cmd line and in UI... (set to false to have your desired behavior)