I have a Web API controller action
[Route("{type}")]
public IEnumerable<Content> Get(ContentType type)
{
...
}
where ContentType is enumeration and type is being provided as a string and not as an integer.
As I've checked the JsonConverterAttribute class which seems it should be possible to put it beside a method parameter
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Enum |
AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Interface |
AttributeTargets.Parameter, // <= HERE!
AllowMultiple = false)]
public class JsonConverterAttribute : Attribute
...
but this doesn't seem to work:
[Route("{type}")]
public IEnumerable<Content> Get([JsonConverter(typeof(StringEnumConverter))] ContentType type)
{
...
}
I'm getting an error:
The parameters dictionary contains a null entry for parameter 'type' of non-nullable type '....ContentType' for method '...' in '...'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
I also don't want to put this converter inside my global configuration as I want my enums to serialize as integers. There are just a few places where I need to deserialize them from strings.
The possible alternative is to pass in a string and parse it manually using Enum.Parse() method but I'd like to avoid it if possible.
Related
I am having trouble modifying a model class used by Swagger.
I would like to add new optional properties within a service response.
So I did modify my class like this :
#ApiModelProperty(required=false,value="This field is optional in response")
public String myNewProp;
Whenever I test my generated code, this field does appear in the response body even when it should not with null value :
{ "newProp": null }
Am I misunderstanding the "required=false" option ?
What did I miss ?
Solved, I just had to add this Jackson annotation for the given field :
#JsonInclude(JsonInclude.Include.NON_NULL)
#ApiModelProperty(required=false,value="This field is optional in response")
public String myNewProp;
I have such controller in my grails project:
def submit() {
def json = request.JSON
Share share = new Share(json)
share.save(flush: true, failOnError: true)
}
Class Share looks like this:
class Share {
String timestamp
String deviceName
String originMessage
Share(JSONObject originalMessage) {
println "Run JSON constructor"
println "$originalMessage"
originMessage = originalMessage.toString()
timestamp = originalMessage.timestamp
deviceName = originalMessage.device
}
It recives JSON request and try to persist in data base.
I get such error in my console on failOnError:
Field error in object 'com.entity.Share' on field 'deviceName': rejected value [null];
Field error in object 'com.entity.Share' on field 'originMessage': rejected value [null]; codes
A lot of dancing around find one possible way: in controller convert JSON to string and pass it in constructor where parameter would be type String and then parse it back in JSON using JSON converter. But why I can not pass JSON Object as parameter correctly. What happens?
I don't see much point in declaring this constructor because domain classes already have an implicit constructor that takes a Map argument. You could call this constructor with the JSONObject because this class implements Map, e.g.
class Share {
String timestamp
String deviceName
String originMessage
}
def json = request.JSON
Share share = new Share(json)
The reason for these errors
Field error in object 'com.entity.Share' on field 'deviceName': rejected value [null];
Field error in object 'com.entity.Share' on field 'originMessage': rejected value [null]; codes
is that your JSONObject instance does not have non-null properties named deviceName or originMessage.
Either you need to figure out why these properties are missing, or allow these properties to be null by adding the following constraints to the domain class
class Share {
String timestamp
String deviceName
String originMessage
static constraints = {
deviceName nullable: true
originMessage nullable: true
}
}
Given a table or view with an Integer column is it possible to do a conversion or cast to a String value in the DBML or create a calculated property on the entity that can be used as a relationship to another entity?
I have tried making the generated type a string but it gives an error:
Error 1 DBML1005: Mapping between DbType 'Int' and Type 'System.String' in Column 'Foo' of Type 'FooRecord' is not supported. 0 0
I don't think you can do anything like this - not in the Linq-to-SQL data model, for sure. If it's an INT in the database, it's an INT in the model and cannot be "manipulated" into being a string all of a sudden...
But what you could do is extend that class that Linq-to-SQL generates for you - it's a partial class, e.g. you can extend it in a separate, second file:
YourEntityEx.cs
public partial class YourEntity
{
public string YourPropertyAsString
{
get { return YourProperty.ToString(); }
set { YourProperty = Convert.ToInt32(value); } // if you even this
}
}
This way, you now have a second property YourPropertyAsString on your YourEntity class that will always be exactly the same as YourProperty - only of type string.
We have a single contract assembly which has all our data contracts. We are using JSON.net for serializing our data contracts to json.
JSON.Net adds both the type name and the assembly name in the $type attribute on serialization. Since all our data contracts are in the same assembly which is always loaded in the current app domain, we should be able to omit this.
How can we achieve this?
Thanks
You can use the Binder property in your JsonSerializerSettings.
This blog post (by the library author) describes the steps: http://james.newtonking.com/archive/2011/11/19/json-net-4-0-release-4-bug-fixes.aspx
In short, you create a custom class deriving from SerializationBinder and override two methods:
BindToName(Type serializedType, out string assemblyName, out string typeName)
BindToType(string assemblyName, string typeName)
The logic you place in those methods will give you direct control over how type names are converted to string representation in the $type field, and how types are located at run-time given values from $type.
In your case, wanting to omit the Assembly name, you can probably do:
public override void BindToName(
Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.FullName;
}
public override Type BindToType(string assemblyName, string typeName)
{
return Type.GetType(typeName);
}
I think maybe tag the class with the JsonObjectAttribute
[DataContract]
[JsonObject("")]
public class MyContractClass { ... }
This should override the fact that it is also a DataContract.
i have this problem: i use the json to send data to server.
All works fine but the problem is a situation like:
public enum SexType
{
Male : 0,
Female : 1
}
class People{
public SexType Sex {get;set;}
}
That create me the json:
{"Sex" : 0}
When i send back to server, this fill the ModelStateError with this issue:
The parameter conversion from type 'System.Int32' to type 'SexType' failed because no type converter can convert between these types.
But if i wrap the value with ' all work well:
{"Sex" : '0'}
Anyone have the same problem?
Tnx for all!
Yes, I got the same problem. The weird problem is that if you sent back:
{"Sex" : 'Male'}
it would deserialize no problem.
To solve the problem, I implemented a custom model binder for enums, leveraging the example found here (slightly modified as there were some errors):
http://eliasbland.wordpress.com/2009/08/08/enumeration-model-binder-for-asp-net-mvc/
namespace yournamespace
{
/// <summary>
/// Generic Custom Model Binder used to properly interpret int representation of enum types from JSON deserialization, including default values
/// </summary>
/// <typeparam name="T">The enum type to apply this Custom Model Binder to</typeparam>
public class EnumBinder<T> : IModelBinder
{
private T DefaultValue { get; set; }
public EnumBinder(T defaultValue)
{
DefaultValue = defaultValue;
}
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
return bindingContext.ValueProvider.GetValue(bindingContext.ModelName) == null ? DefaultValue : GetEnumValue(DefaultValue, bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue);
}
#endregion
public static T GetEnumValue<T>(T defaultValue, string value)
{
T enumType = defaultValue;
if ((!String.IsNullOrEmpty(value)) && (Contains(typeof(T), value)))
enumType = (T)Enum.Parse(typeof(T), value, true);
return enumType;
}
public static bool Contains(Type enumType, string value)
{
return Enum.GetNames(enumType).Contains(value, StringComparer.OrdinalIgnoreCase);
}
}
}
and then registering the model binder in global.asax.cs.
In your case it would be something like:
ModelBinders.Binders.Add(typeof(SexType), new EnumBinder<SexType>(SexType.Male));
I am not sure if there is a quicker way, but this works great.
The Model binding uses the Enum.Parse() method, which is fairly smart about interpreting strings but does NOT explicitly cast or convert other types into strings, even if system-level facilities exist to do so and even if they're the internal storage type used within the Enum.
Is this the right behavior? Arguably so, since if you don't know enough to convert your Enum values to strings you might not be aware that the right-hand side of the Enum values are not necessarily unique within the Enum, either.
As a matter of personal taste (and this is probably also because I do way too much statistical analysis programming) for sex I generally prefer to define it as a clear boolean value, i.e. instead of differentiating between arbitrary values for 'Male' and 'Female' I use a variable called e.g. IsFemale and set it to true or false. This plays more nicely with json, since it relies on primitive types common to both languages, and requires less typing when you want to use it.