Deserialize nested Kotlin object in springMVC - spring

I'm trying to deserialize a complex GET request into a structure of nested objects.
The GET requests looks like:
curl 'localhost:8080/?id=1&inner.id=1'
and the code should look like this:
class RootObj(val id: Int, inner: InnerObject)
class InnerObject(val id: Int)
#RestController
class SearchController {
#GetMapping(path = ["/"])
fun getRoot(rootObj: RootObj): String {
return "ok"
}
}
This doesn't work out of the box I guess because spring doesn't know how properly create this nested structure.
Parameter specified as non-null is null: [...] parameter inner","path":"/"}%
Is there a way to overcome this problem? Maybe providing a totally custom deserializer code?
As alternative solution, I guess I could flatten the object hierarchy but for doing so I must be able to map a query parameter like inner.id to a field named innerId

Spring can actually map the query params to the custom object directly, but you need to provide defaults to the params of the custom object constructor.
So you need to define your classes as below for it to work
class RootObj(val id: Int = 0, val inner: InnerObject = InnerObject(0))
class InnerObject(var id: Int = 0)
Do note that the id field of InnerObject would have to be declared as var for Spring to be able to map it. Then curl 'localhost:8080/?id=1&inner.id=1' would work fine.

Related

Custom serialization of specific return type for DataFetcher

I am using Spring for GraphQL (version 2.7.0-M1).
In my domain model, a lot of properties return an object Foo. This object must be serialized to a String based on data available from GraphQlContext. So the schema looks like:
type Parent {
code: String!
foo: String
...
}
It is easy to do this with #SchemaMapping for a specific parent type.
#SchemaMapping(typeName = "Parent", field = "foo")
public String foo(Parent parent, DataFetchingEnvironment env) {
var context = env.getGraphQlContext();
return ...
However, this is not very DRY. I am looking for a way to have this code at one place, like a custom scalar.
Is there a way to do this with spring-graphql / graphql-java?
Example
An example is a Localized<T> object we use. For instance a Product instance has Localized<String> properties for title and description (and more).
For the GraphQL query we can set the context, part of the context is the Locale. For all Localized property values the value can be converted to the string value for the locale. We are looking for a way to do this automagically. Otherwise it creates a lot of boiler plate code
Would #ContextValue help here? This would remove a bit of boilerplate from your controller handlers.
#SchemaMapping(typeName = "Parent", field = "foo")
public String foo(Parent parent, #ContextValue Foo foo) {
If you'd like something more involved, I think you should elaborate on the exact relationship between an attribute of one or multiple types in your schema, and some random value in the context.
Maybe you could come up with some concrete example here?

Kotlin Spring Request Blank Validation Best Practice

I'm new to kotlin and so far I have 2 options to do not blank validation on incoming HTTP request since a blank string considered as valid value in kotlin null checking.
Validation on class init function
data class Foo(val key: String) {
init {if (this.key.isEmpty()) throw Exception("Invalid Request")}
}
Validation with javax annotation
data class Foo(#field.NotBlank val key: String)
Both ways are working as expected, I just curious on what's the best way to do this in kotlin. I'm afraid that my kotlin code is to java, at that point no use I work with kotlin
If you want to validate the incoming request, go for 2nd option.
Consider having more than one attribute, lets say you have class such as:
data class User(val name: String, val surname:String)
Then you have to write validation for each field. What if both name and surname are blank? The name validation throws an error, you add name to request and BAM, surname validation throws an error.
data class User(val name: String, val surname:String) {
init {
if (this.name.isEmpty()) throw Exception("Name is missing")
if (this.surname.isEmpty()) throw Exception("Surname is missing)
}
}
You can validate all at once, using OR for example, but then you would lose explicit error - what was wrong? Blank name? Blank surname? Both were blank...?
data class User(val name: String, val surname:String) {
init {
if (this.name.isEmpty() || this.surname.isEmpty()) throw Exception("Name or surname is missing")
}
}
Now think about three, four, five fields.
If you use javax.validation properly, you have to write just the annotations and the rest is done by the framework - it will explictly say, what is wrong, on which field, because it checks all the constraints and if there are any violations, it throws an error with all violation details.

How to validate RequestBody with nullable fields when all properties are empty/null values

I would like to know if there is a better way then writting a check at the beginning of a method.
This question is based on the following code:
Class Pojo:
data class PojoX(val name: String?, val city: String?)
Controller:
#PatchMapping("/demo")
fun update(#RequestBody request: PojoX): PojoDTO{
if (request.name == null && request.city == null) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Atleast one field should be filled")
}
// Do somthing if atleast one field is filled...
}
I want the code to be maintainable easily without adding new check when a new property is added to the request class.
Is there an annotation in Spring which can check if all fields are null?
You'd need to write a custom Validator that is only valid if at least one of the fields is not null (i.e. encapsulating your current check into a validator and then use the #Valid annotation.

Custom serializer with django 2

I would like to create a serializer that given an input JSON object select certain attributes.
There is an legacy system that sends an object, for example:
{
"a": {
"b": "test"
}
}
I need to keep certain properties of the object. For example a.b. I have created the following serializer, but it is not working:
class CustomSerializer(serializers.Serializer):
b = serializers.CharField(source='a.b', required=True)
Does anyone know what is failing?
Thanks!
You need to implement nested serializer. For your example, you can try something like this :
class BSerializer(serializers.Serializer):
b = serializers.CharField()
class ASerializer(serializers.Serializer):
a = BSerializer()
Note : this is just an example of nested serializer. You might need to add few things to make it work properly for you (adding Meta, other fields if needed, use it in view etc. )

Passing json "data" array in Retrofit 2

I'm trying retrofit 2 for the first time and I have no idea how to tell it to get "Category" objects from an jsonarray named "data".
Method 1
If I do it like this it fails:
#GET("category")
Call<List<Category>> listCategories();
Method 2
But when I make a new model, called "Categories", which holds a List and is annotated with #SerializedName("data"), it works flawlessly.
#GET("category")
Call<Categories> listCategories();
My Question
Should I annotate something in the interface, like this
#GET("category")
#Annotation to look inside "data"
Call<List<Category>> listCategories();
Or should I annotate my "Category" model to tell Retrofit (or GSON)
that it lives inside the json array "data"?
JSON
{"data":[{"id":1,"name":"Fist Name","parent":0},{"id":2,"name":"Second Name","parent":1}]}
Method 2 Is correct and we use it when we dont want to use/define the json response object/arrays key names(field names). instead provide our own. Eg. In below code List object name is items but while Serialization and Deserialization it uses, what you have defined in #SerializedName annotation that is data.
public class Categories {
//solution 1
List<Category> data;//object name must match with the json response
//solution 2
#SerializedName("data")
List<Category> items;
}
Should I annotate something in the interface
No. There is no such annotation available and everything you can do is only in Response type class.

Resources