#JsonAppend virtual property – does ObjectReader honor it? - jackson-databind

There's documentation on how to use #JsonAppend to add a virtual property to a POJO when serializing it to JSON. However, no Javadoc or article says anything about deserialization. Suppose we add a "schemaVersion" property to an object:
#JsonAppend(attrs = { #JsonAppend.Attr(value = "schemaVersion") })
public static class MixinWithVersion {}
How do we read objects that have that property set? I'm getting UnrecognizedPropertyException and wondering if it's even possible to read back in an object with an added property.

Adding #JsonIgnoreProperties solves the problem on deserialization:
#JsonAppend(attrs = { #JsonAppend.Attr(value = "schemaVersion") })
#JsonIgnoreProperties({ "schemaVersion" })
public static class MixinWithVersion {}

Related

Modifying the DTO name appearing in OpenAPI (Swagger) schemas in NestJS

I am facing a problem where my DTO types are named one thing, but I want them to appear with a different name in the OpenAPI doc page.
For example, I have a UserDto class that I use in my controller, but wanted it to appear as simply "User" in the schemas section (and everywhere else this applies). Is that possible? Is there any decorator I can use?
I know I can simply modify the class name, but there is already a different user class used elsewhere.
I have searched everywhere with no avail.
BTW, I am using typescript and nestjs.
Every help will be appreciated, thanks!
Out of the box, Nest.js doesn't yet offer a ready-made solution. There is an open pull request (as mentioned earlier) https://github.com/nestjs/swagger/pull/983, but when it will be merged is unknown.
You can change the DTO name in schemas using one of the following approaches:
Add a static name property to your DTO.
class UserDto {
static name = 'User'; // <- here
#ApiProperty()
firstName: string;
// ...
}
But in strict mode, TypeScript will show an error like:
Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'UserDto'.
Write a decorator with an interface as suggested in the pull request and use it until the desired functionality appears in Nest.js.
The decorator adds the name property with the needed value to the wrapper class for the DTO.
type Constructor<T = object> = new(...args: any[]) => T;
type Wrapper<T = object> = { new(): (T & any), prototype: T };
type DecoratorOptions = { name: string };
type ApiSchemaDecorator = <T extends Constructor>(options: DecoratorOptions) => (constructor: T) => Wrapper<T>;
const ApiSchema: ApiSchemaDecorator = ({ name }) => {
return (constructor) => {
const wrapper = class extends constructor { };
Object.defineProperty(wrapper, 'name', {
value: name,
writable: false,
});
return wrapper;
}
}
Use as suggested in the proposal:
#ApiSchema({ name: 'User' }) // <- here
class UserDto {
#ApiProperty()
firstName: string;
// ...
}
And don't forget that in TypeScript 5 the decorator API will change to something close to the implementation in JavaScript 😉
I solved in my case using #ApiModel
like this
#ApiModel(value="MeuLindoDto")
public class NameOriginalClassResponseDto ...

Aqueduct ORM ManagedObject's transient property being persisted

I have an Aqueduct project using the ORM, with a data model as follows:
class _Thing {
#primaryKey
int id;
String first;
String second;
}
class Thing extends ManagedObject<_Thing> implements _Thing {
#Serialize()
OtherThing get firstAndSecond() {
// return some value computed from first and second
}
#Serialize()
set firstAndSecond(OtherThing firstAndSecond) {
// set first and second based on some computation
}
}
According to the docs for transient properties, annotating with #Serialize() should enable this model to be serialized/deserialized. It also says that properties in ManagedObjects are not persisted, but when I run the server, I get the error:
Data Model Error: Property 'firstAndSecond' on 'Thing' has an unsupported type.
If I remove the #Serialize(), it doesn't try to persist it, but I can't serialize/deserialize this object.
Any suggestions as to why this is happening or how I can control this behaviour?
This should be in the docs -
A Serializable property must be a primitive type (e.g. String, int, double, bool or a Map or List containing these types). Serializable values are passed directly to the codec that is reading from a request body or writing to a response body (by default, this codec is JSON). In the case of a custom type like OtherThing, the codec doesn't know how to encode or decode that type.
For complex types, you might use a map:
#Serialize()
Map<String, dynanic> get firstAndSecond() {
return {"first": first, "second": second};
}
You might also use CSV-like data:
#Serialize()
String get firstAndSecond() {
return "$first,$second";
}

Get the name of a Dart class as a Type or String

Problem I need to solve
Is there a way to get the class name of a dart class as a String or a Type object..?
class MyClass {
}
var myClass = MyClass();
I know the property, runtimeType which return the type of the object as a Type object. But is there a similar function for classes?
print(myClass.runtimeType.toString());
What I currently do is creating an object of the class and use runtimeType.
String type = MyClass().runtimeType.toString();
Note: In python there is a variable called __name__ in every class, which does what I need.
My intention
My final goal is to create dart objects using previously saved class names. In this issue they have proposed a method using Maps.
The thing is that I have lots of classes and that method looks messy in my situation.
What I currently do is, save the object type by:
var saving = myClass.runtimeType.toString();
And when loading:
if (saving == MyClass().runtimeType.toString()) {
return MyClass();
}
From your experiences and opinions, can you propose a better solution?
You can use:
var runtimeTypeName = (MyClass).toString();
or for generics:
var runtimeTypeName = T.toString();
The class type can be used as a Type:
Type myType = MyClass;

Inherited grails domain classes missing dynamic properties

I'm having a problem where the related table id fields return 'null' from my domain objects when using inheritance. Here is an example:
In /src/groovy/
BaseClass1.groovy
class BaseClass1 {
Long id
static mapping = {
tablePerConcreteClass true
}
}
BaseClass2.groovy
class BaseClass2 extends BaseClass1 {
String someOtherProperty
static constraints = {
someOtherProperty(maxSize:200)
}
static mapping = BaseClass1.mapping
}
In /grails-app/domain
ParentClass.groovy
class ParentClass extends BaseClass2 {
ChildClass myChild
static mapping = BaseClass2.mapping << {
version false
}
}
ChildClass.groovy
class ChildClass extends BaseClass1 {
String property
static mapping = BaseClass1.mapping
}
The problem appears here:
SomeotherCode.groovy
print parentClassInstance.myChild.id // returns the value
print parentClassInstance.myChildId // returns null
Any ideas what might be going on to get those dynamic properties to break like this?
After debugging into the get(AssociationName)Id source, I found the following:
The handler for this is:
GrailsDomainConfigurationUtil.getAssociationIdentifier(Object target, String propertyName,
GrailsDomainClass referencedDomainClass) {
String getterName = GrailsClassUtils.getGetterName(propertyName);
try {
Method m = target.getClass().getMethod(getterName, EMPTY_CLASS_ARRAY);
Object value = m.invoke(target);
if (value != null && referencedDomainClass != null) {
String identifierGetter = GrailsClassUtils.getGetterName(referencedDomainClass.getIdentifier().getName());
m = value.getClass().getDeclaredMethod(identifierGetter, EMPTY_CLASS_ARRAY);
return (Serializable)m.invoke(value);
}
}
catch (NoSuchMethodException e) {
// ignore
}
catch (IllegalAccessException e) {
// ignore
}
catch (InvocationTargetException e) {
// ignore
}
return null;
}
It threw an exception on the related class (value.getClass().getDeclaredMethod), saying NoSuchMethod for the method getId(). I was unable to remove the id declaration from the base class without Grails complaining that an identifier column was required. I tried marking id as public and it also complained that it wasn't there. So, I tried this
BaseClass {
Long id
public Long getId() { return this.#id }
}
and things worked on some classes, but not on others.
When I removed the ID declaration, I go an error: "Identity property not found, but required in domain class". On a whim, I tried adding #Entity to the concrete classes and viola! everything started working.
class BaseClass {
//Don't declare id!
}
#Entity
class ParentClass {}
#Entity
class ChildClass {}
I still think it is a grails bug that it needs to be added, but at least it is easy enough to work around.
I'm not sure why you are seeing this behavior, but I'm also not sure why you are doing some of the things you are doing here. Why have a domain class extend a POGO? Domains, Controllers, and Services are heavily managed by the Grails machinery, which probably was not designed for this sort of use. Specifically, I believe Grails builds the dynamic property getters for the GrailsDomainProperty(s) of GrailsDomainClass(es), not POGO's. In this case, you have an explicitly declared id field in BaseClass1 that is not a GrailsDomainProperty. I suspect that this POGO id property is not picked up by the Grails machinery that creates the dynamic property getters for Domains.
You might try putting BaseClass1/2 in /grails-app/domain, perhaps making them abstract if you don't want them instantiated, then extending them as you are and seeing if you observe the behavior you want.

mapping a one-to-many where the map key is an int

I am trying to understand the annotations MapKey and MapKeyColumn and I have found them confusing. I was reading an article that made me even more confused (The specification section)
I have an entity with an int field and it is not the primary key:
public class Connections{
...
public final int getConnectionId() {
return this.connectionId;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_StartpointTNA")
public final Endpoint getStartpoint() {
return this.startpoint;
}
...
}
and in the other side I have
public class Endpoint{
...
#OneToMany(mappedBy = "startpoint", fetch = FetchType.LAZY, cascade = { javax.persistence.CascadeType.REMOVE })
#MapKeyColumn(name = "connectionId")
public Map<Integer, Connections> getConnections() {
return this.connections;
}
....
}
I dont know really how to fix this. I keep getting: org.apache.openjpa.persistence.ArgumentException: "connections" declared that it is mapped by "startpoint", but that is a not a field of the related type.
what is the proper way to map this?
As someone posted to the JIRA you opened, get rid of the final on your methods.
From the JPA 2 spec:
(Section 2.1 "The Entity Class", page 21) states: "The entity class must not be final. No methods or persistent instance variables of the entity class may be final."
I had the same issue and getting the same error-message
but in my case I was wrong with
mappedBy = "foo"
where foo must declare the field! Not the column.

Resources