Using Swagger-annotation to represent a complex property as a String - java

In a Java/Spring ReST application, I'm using swagger-annotations 1.3.7 I have a number of small classes (for example, GenderCode) that I use as properties in my ReST models. These classes have a single public property, called value. Using Jackson, my APIs can accept a simple String s and construct an instance of, say, GenderCode with its value set to s. Similarly, it can serialize a GenderCode as a simple String (which of course represents the value of value).
I would like my Swagger documentation to represent these objects as simple strings, since that represents what the JSON will look like. Instead it represents an complex type with a "value" key:
{
"genderCode": {
"value": ""
},
...
}
It should look simply like this:
{
"genderCode": "",
...
}
Here's what the Java model would look like:
public class Person {
#JsonProperty("genderCode")
#Valid
#KnownEnumValue
#ApiModelProperty(value = "GenderCode", dataType="string", required = false,
allowableValues=GenderCode.POSSIBLE_VALUES_DISPLAY)
private GenderCode genderCode;
...
}
Here's the definition of that property within the API definition file that Swagger generates:
"genderCode":{"enum":["ANY","M","F"],"description":"GenderCode","required":false,"type":"GenderCode"}
I've tried using an OverrideConverter, but that had no effect. Any thoughts on how this can be done?

Related

Converting TypeScript enum to JSON to Java enum and back

In an Angular2 application (using Angular 13 and Typescript 4.5.5) we use a lot of string enums like this:
export enum Language {
de = "German",
fr = "French",
it = "Italian",
en = "English"
}
export class Request {
language: Language = Language.de;
... // other enum-valued properties like the above
}
The value of the enum (e.g. "Italian") is the value that should be displayed to the user, e.g. in a drop-down. The enum key (e.g. 'it') is what we would like to store in the database when we submit the data to our backend REST service that is implemented in Java.
So in the backend service we have a corresponding Java enum defined as this:
public enum Language {
de, fr, it, en
}
#Entity
public class Request {
#Enumerated(EnumType.STRING)
private Language language;
... // other persistent properties
}
The problem is that when the data is submitted to the backend service, the Typescript enum is serialized to JSON using its value. So the service receives "Italian" instead of "it" and cannot map it to the Java enum.
Similarly, when we try to retrieve data from the backend service to display it in the Angular GUI, the service sends "it" instead "Italian", so the deserialization of JSON to the TypeScript enum also fails.
Is there any way to tell TypeScript that it should use the enum key both when serializing and deserializing an enum to/from JSON?
And more generally, which approach would you suggest for what we're trying to achieve (i.e. separate the display value shown to the user from the technical value to be stored in the database)?
I sadly still can't comment but... as a reply now:
I'm presuming you are working with either Angular Material or PrimeNG as a GUI.
With both options you have e.g. for a dropdown the ability to set the value you want to display to the user (in your case "Italian") to a different variable than the value you want to have changed /stored (in your case "it").
ex. prime ng:
[https://www.primefaces.org/primeng-v11-lts/#/dropdown][1]
<p-dropdown [options]="languages" [(ngModel)]="selectedlanguage" optionLabel="nameFull" optionValue="nameShort"></p-dropdown>
ex angular material:
[https://material.angular.io/components/select/overview#getting-and-setting-the-select-value][1]
<mat-select [(ngModel)]="selectedlanguage" name="language">
<mat-option *ngFor="let language of alllanguages" [value]="language.nameShort">
{{language.nameLong}}
</mat-option>
</mat-select>
.js:
interface Languages {
name: string;
namelong: string;
}
string selectedlanguage;
Languages alllanguages[] = // as an with all your values.
Hope this helps :)

Deserializing JSON containing invalid class identifiers

In Java, using Jackson, I want to deserialize JSON that looks something like this:
{
"123_ABC": {
"XYZ": 768,
"123_DATA": {
"123_DEF": "",
"123_ACT": "ZAC",
"123_PAG": {
"123_PAG_A": 1,
"123_PAG_B": 1
}
}
}
}
You all know that identifiers starting with a number are invalid in Java (and every programming language I ever heard of.)
I already know how to use #JsonProperty to translate field names, but handling class names is outside my knowledge.
If I define classes corresponding to the structure of the JSON, but with valid class names, is there a way to use Jackson annotations to map the invalid class id in the JSON to my valid class names?
I think #JsonProperty should be good to deserialize this.
Let's create a wrapper class that will have 123_ABC as a property of class ValidClass.
class Wrapper {
#JsonProperty("123_ABC")
private ValidClass validName;
}
Now, when you serialize, it will create JSON like this (or can be deserialized using that)
{ "123_ABC":{ //PROPERTIES OF ValidClass HERE } }
Similarly, you can have different properties in further inner classes.
In case if you to support 123_ABC only for deserialization and serialize with correct field names, you can do like this
#JsonAlias("123_ABC")
private ValidClass validName;
it will serialize to following.
{"validName": {//properties}}
but deserialization can be done using both
{"validName": {//properties}}
{"123_ABC": {//properties}}
In case, if keys keep changing, I would suggest to deserialize them in Map.

how to convert json property like "oData.type" to java object [duplicate]

What is the basic purpose of #SerializedName annotation in Android using Gson?
Give me some different examples. I can't understand the main purpose of using it.
Java class example,
public class Person {
#SerializedName("name")
private String personName;
#SerializedName("bd")
private String birthDate;
}
This class has two fields that represent the person name and birth date of a person. These fields are annotated with the #SerializedName annotation. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field personName is represented as name in JSON.
JSON Example,
{
"name":"chintan",
"bd":"01-01-1990"
}
There are already few answers here,but I would like to add that if you are using ProGuard to Obfuscate your code & don't use #SerializedName("name") in your model class, then your GSON won't work. Because due to obfuscation, your variable names might have changed from String name to String a resulting into broken GSON parsing as GSON will look for key a into json & it will fail.
By specifying #SerializedName, GSON will not look in json based on variable name & will just use specified #SerializedName.
Of Course you can tell proguard to not obfuscate your model, but if you would like to have model obfuscated, then you must specify #SerializedName
Using #SerializedName you are actually telling the Parser when receiving a callback from the server i.e. of a Json format:
{
"name":"John Doe",
}
that when Serializing or Deserializing an object to instead of searching for a key named: "userName", in the Json response, to search for "name".
#SerializedName("name")
var userName: String,
This is good because you may have a model that you would like it to have its members being called with whatever you like.
You can instruct Proguard to not obfuscate your data classes by specifying #Keep on top of the class. This will neither remove nor obfuscate your class. No need to add #SerializedName to each and every field explicitly if the field name is similar to the Json key being used for it.
Let's say in a real-world scenario, your backend dev is giving you this response for an API request you make
{
"name":"John Doe",
"id":"1478"
}
Now, in the data class you make to handle this, there might be chances you want to specify a different variable name at Android side for the fields "name" and "id" that you are getting from backend.
#SerializedName comes to rescue here.
You just need to specify the actual key value you will be getting from backend in the #SerializedName (which will be used to serialize and deserialize) and then you can use a variable name of your choice that stores that value received from the operation.
For example, for the JSON I mentioned earlier, here is how its data class will look like:
data class User(
#SerializedName("name") val userName: String,
#SerializedName("id") val userId: Int
)
Here name, id is used in #SerializedName because it's the backend key.
But I have used userName, userId to store those values.

Response does not contain zero integers

I have restful API and use Jetty as a server. I send a post request to create my object which contains some skill list. Skill contains of String id and Integer value fields. When I use 0 or "0" for my Integer field with the get response I get the skill array without value field at all.
#XmlRootElement
#JsonAutoDetect(isGetterVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE, fieldVisibility = Visibility.NONE)
public class Skill
{
#com.fasterxml.jackson.annotation.JsonProperty(value="id")
#javax.validation.constraints.NotNull
private java.lang.String _id;
#com.fasterxml.jackson.annotation.JsonProperty(value="value")
#javax.validation.constraints.NotNull
private java.lang.Integer _value;
// getters and setters
}
My request body is like this:
{
// some other fields
"skills": [{
"id":"Swedish",
"value":0
},{
"id":"Finnish",
"value":"0"
}]
}
After applying necessary changes to my object I pass it to be returned via this line:
Response.ok().entity(myObject).build();
The body of the get response is like this:
{
// some other fields
"skills" : [ {
"id" : "Finnish"
}, {
"id" : "Swedish"
} ]
}
With other values everything works fine, however, 0 seems to be so special that it doesn't even include this field to the object.
The question is Why and How can I solve it?
The problem is not in Jetty, Jersey or YaaS. The problem seems to be in Jackson. Jackson does the serialization/deserialization and seems to have some optimization, thus zeros are skipped.
Unfortunately I haven't found any resource yet which says exactly why would you skip 0 and I didn't manage to find the place in Jackson code where this happens.
Possible solutions:
Use annotation #JsonInclude(JsonInclude.Include.ALWAYS) for your field and it will not be skipped.
Don't allow zeros for your Integer field.
Use String type instead of Integer.
The problem is that my object is generated by YaaS, thus, I cannot just change by generated object and not sure if YaaS has the possibility to generate the object with the annotation from the 1 item. I assume that in this case the 2 option might be the best.
The problem is in version of Jackson library.
When we add JsonFeature by default we have serializeJsonEmptyValues=false.
In this case in the method JsonFeature#initDefaultObjectMapper we will come to the point where we do objMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
If we have a look to the javaDoc of NON_EMPTY field, we will see the following:
Compatibility note: Jackson 2.6 included a wider range of "empty"
values than either earlier (up to 2.5) or later (2.7 and beyond)
types; specifically: Default values of primitive types (like 0 for
int/java.lang.Integer and false for bool/Boolean) Timestamp 0 for
date/time types With 2.7, definition has been tightened back to only
containing types explained above (null, absent, empty String, empty
containers), and now extended definition may be specified using
NON_DEFAULT.
So, which means that if you use the version 2.6 the zeros will disappear. And this happens to be in our project because we use Redisson which uses the version 2.6. of Jackson library.
you have different value types for similar keys:
int 0 for "value" of "Swedish" and String "0" for "value" of "Finnish".
This may cause problem if some kind of object by fields building is involved.
I had the same problem but I didn't use #javax.validation.constraints.NotNull.
What I had was
ObjectMapper objectMapper = new org.codehaus.jackson.map.ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
MyClass myClass = new MyClass();
myClass.setStringField("some value");
myClass.setIntField(0);
String json = objectMapper.writeValueAsString(myClass);
System.out.println(json); // --> { "stringField" : "some value" }
It didn't print the intField with value zero. All I had to change was the NON_EMPTY rule to this:
objectMapper.setSerializationInclusion(Include.NON_ABSENT);
String json = objectMapper.writeValueAsString(myClass);
System.out.println(json); // --> { "stringField" : "some value" }

What is the basic purpose of #SerializedName annotation in Android using Gson

What is the basic purpose of #SerializedName annotation in Android using Gson?
Give me some different examples. I can't understand the main purpose of using it.
Java class example,
public class Person {
#SerializedName("name")
private String personName;
#SerializedName("bd")
private String birthDate;
}
This class has two fields that represent the person name and birth date of a person. These fields are annotated with the #SerializedName annotation. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field personName is represented as name in JSON.
JSON Example,
{
"name":"chintan",
"bd":"01-01-1990"
}
There are already few answers here,but I would like to add that if you are using ProGuard to Obfuscate your code & don't use #SerializedName("name") in your model class, then your GSON won't work. Because due to obfuscation, your variable names might have changed from String name to String a resulting into broken GSON parsing as GSON will look for key a into json & it will fail.
By specifying #SerializedName, GSON will not look in json based on variable name & will just use specified #SerializedName.
Of Course you can tell proguard to not obfuscate your model, but if you would like to have model obfuscated, then you must specify #SerializedName
Using #SerializedName you are actually telling the Parser when receiving a callback from the server i.e. of a Json format:
{
"name":"John Doe",
}
that when Serializing or Deserializing an object to instead of searching for a key named: "userName", in the Json response, to search for "name".
#SerializedName("name")
var userName: String,
This is good because you may have a model that you would like it to have its members being called with whatever you like.
You can instruct Proguard to not obfuscate your data classes by specifying #Keep on top of the class. This will neither remove nor obfuscate your class. No need to add #SerializedName to each and every field explicitly if the field name is similar to the Json key being used for it.
Let's say in a real-world scenario, your backend dev is giving you this response for an API request you make
{
"name":"John Doe",
"id":"1478"
}
Now, in the data class you make to handle this, there might be chances you want to specify a different variable name at Android side for the fields "name" and "id" that you are getting from backend.
#SerializedName comes to rescue here.
You just need to specify the actual key value you will be getting from backend in the #SerializedName (which will be used to serialize and deserialize) and then you can use a variable name of your choice that stores that value received from the operation.
For example, for the JSON I mentioned earlier, here is how its data class will look like:
data class User(
#SerializedName("name") val userName: String,
#SerializedName("id") val userId: Int
)
Here name, id is used in #SerializedName because it's the backend key.
But I have used userName, userId to store those values.

Categories

Resources