Spring/Kotlin: How do I make a missing JSON field deserialize to the default value given by my entity class, and not to null? - spring

I have an entity, defined using a data class:
#Entity
data class BlogPost(
(...)
val title: String,
(...)
val slug: String = title.toSlug(),
)
As per this answer, I expected that a JSON object without a slug field would deserialize to a BlogPost whose slug was created using my .toSlug() extension method – but, to my surprise, the deserialized .slug ends up being null even though I thought it wasn't nullable (it's a String, not a String?).
I've tried using #JsonInclude(Include.NON_NULL), but I suspect this is the wrong tactic, and it only seems to affect serialization; the missing JSON value is still deserialized to null rather than my default.
I'm on Spring Boot 2.4.3.

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?

GraphQL: No child/nested data available in response when parent is null?

I am facing problems with nullable fields when trying to use the data returned when executing my query.
Schema
A simplified schema for demonstration purpose:
type Query {
members: [Instrument!]!
}
type Instrument {
series: SeriesType!
...
}
type SeriesType {
dividendYield: SeriesMethods
...
}
type SeriesMethods{
latest: Float!
...
}
There is nullable data that is resolved at dividendYield and a typical response I get when executing the query is shown in the following section.
Typical Response
A picture of the query and response can be seen here.
So what is the problem???
We are using this data to for visuals on a web app. The data is read in as an object such that you are able to access the data by data.series.dividendYield.latest. The problem comes in when dividendYield returns null as there is there no longer exists a latest field anymore.
For the client side it will always be necessary for the field to be present in the data, even if the parent resolver is null. Is it possible for the resolvers to be setup such that all children fields also return null rather than not appearing in the data response? Any other solutions would also be appreciated.

Deserialize nested Kotlin object in springMVC

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.

How to require propertyA OR propertyB in a GraphQL Schema

In the type definition below, is there a way to require name or model, instead of name and model?
type Starship {
id: ID!
name: String!
model: String!
length(unit: LengthUnit = METER): Float
}
I may have name or model due to some legacy data limitations. I would rather enforce this at the GraphQL validation layer, rather than in code.
EDIT:
There is some good discussion about adding validation to the graphQL spec, which you can read here: https://github.com/graphql/graphql-js/issues/361
There are also a couple of libraries to extend validation:
https://github.com/xpepermint/graphql-type-factory
https://github.com/stephenhandley/graphql-validated-types
I'm going to stick with validating the types in code, at least until they add better support.
You could try to use union to represent name or model concept . As union only works with object type now , that means you have also model name and model as object type first.
Code wise the schema looks like :
type Name {
value : String!
}
type Model {
value : String!
}
union NameOrModel = Name | Model
type Starship {
id: ID!
nameOrModel : NameOrModel!
length(unit: LengthUnit = METER): Float
}
It is very ugly IMO as it introduces many unnecessary noise and complexity to the schema .So I would prefer to stick with your original schema and do that check manually in the backend.
From the spec:
By default, all types in GraphQL are nullable; the null value is a valid response for all of the above types. To declare a type that disallows null, the GraphQL Non‐Null type can be used. This type wraps an underlying type, and this type acts identically to that wrapped type, with the exception that null is not a valid response for the wrapping type. A trailing exclamation mark is used to denote a field that uses a Non‐Null type like this: name: String!.
An individual field may be nullable or non-nullable. Non-null validation happens at the field level, independent of other fields. So there is no mechanism for validating whether some combination of fields are or are not null.

EmbeddedDocument field never optional in Django Mongoengine REST framework

I am using Django REST framework with Mongoengine. When I attempt serialize an optional field on an embedded document, the framework still requires the field and returns the error message that the field cannot be left blank. How do I make fields optional on an EmbeddedDocument? It works fine for standard Document model objects, just not for EmbeddedDocument objects.
My model:
class Event(EmbeddedDocument):
id = ObjectIdField(required=True, default=ObjectId())
status = StringField(required=True, max_length=50)
note = StringField(required=False, max_length=2000)
created = DateTimeField(required=True, default=timezone.now())
My serializer:
class EventSerializer(EmbeddedDocumentSerializer):
class Meta:
model = Event
depth = 2
def validate(self, data):
return data
Note that the field "note" is set to required=False. When I serialize the document, however, I still get an error message that the field can't be left blank. Thank you!
I came across the same problem, I think you can mark the fields as blank=True and it should allow you to place nothing in those fields.

Resources