How to deserialize enum by implemented interface without knowing sub-classes - kotlin

I am writing a custom spring boot starter that provides a uniform error response class for all repositories that will add this starter. It also provides the corresponding exception handler.
The problem is, that this error response needs an error code which might differ between all those repositories, using my starter. So the solution would be to create an error response with an error code that is an interface. Other repositories can then create enums implementing this interface to achieve the desired behavior.
It is written in kotlin
interface BaseErrorCode {
val message: String
}
class ErrorResponse {
val customMessage: String,
val errorCode: BaseErrorCode,
val timestamp: OffsetDateTime
}
Now, in another repository, I use this starter, get access to the classes above and create my error code enum:
enum class MyCustomErrorCodes : BaseErrorCode {
FOO
}
Now, I can throw an exception and through the handler, this json will be produced:
{
"customMessage": "My message",
"errorCode": "FOO",
"timestamp": "2023-01-27T12:15:31.7730645+01:00"
}
So serializing works absolutely fine.
However, when deserializing the ErrorResponse in my integration-tests, I get the following exception:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `my.package.BaseErrorCode` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"customMessage":"My message","errorCode":"FOO","timestamp":"2023-01-27T12:12:06.4932227+01:00"}"; line: 1, column: 54] (through reference chain: my.package.ErrorResponse["errorCode"])
Approach 1
I know a solution where you write a custom deserializer. But this could not be placed in the spring boot starter because I need to know the implementing classes of that ´BaseErrorCode`-interface.
Approach 2
Using #JsonSubType, I can also tell Jackson how to handle this interface. But I do need some information about implementing enums as well, which I do not have in my starter
Is there any way to deserialize this error response
only by modifying the starter, not each repository
without knowing the classes that implement BaseErrorCode, BUT knowing that it will ALWAYS be an enum?

I think the answer lies in using intentionally dissimilar objects. I have not tested, but I think:
follow what you have with serialization, and note that you are not serializing the whole enum rather an instance of enum which conforms to BaseErrorCode.
on deserialization you make a concrete implementation of BaseErrorCode (this can be private to the Starter) that you deserialize the JSON in to. (Adding #JsonIgnoreProperties(ignoreUnknown = true) to guard against an unknown subclass sending more data than you can deal with).

Related

Why do I need an in-modifier in a generic to make it accept super classes?

I am working with some code that utilises generics, and I have a hard time trying to understand why a certain code work.
I have made a short example wherein the "error" can be replicated:
sealed class Sealed {
class Klass : Sealed()
}
class Generic<T>
fun main(){
val map = mutableMapOf<String,Generic<Sealed.Klass>>()
map.put("hello", Generic<Sealed>())
}
as you can see, my mutableMap takes another generic. in the put-method, I pass in the superclass of Klass into the Generic class.
This doesn't work, and I think for obvious reasons: Klass is an specific implementation of Sealed, so the map only wants that.
So, to actually get to accept Sealed a generic parameter, I can modify the map's generic type to "in":
val map = mutableMapOf<String,Generic<in Sealed.Klass>>()
I'm pretty sure I get what in does (it only lets the class accept T as an input parameter), but I do not understand why it makes this example work, since I am not specifically using T as an input/output
Can anyone help me understand?
You're not specifically using T as an input/output, but the API of Map does. Because Map<Generic<Sealed.Klass>>.get() returns Generic<Sealed.Klass>?, adding a Generic<Sealed> to the collection could cause get to return something with an unacceptable type. Using in projection will prevent you from using methods on Map which could cause this problem, which makes the code safe to compile.

How to dependency-inject an interface with multiple generic types in asp.net core [duplicate]

I am using WebAPI in ASP.NET Core.
This works:
services.AddScoped<IApiKeysService, APIKeysService>();
Now that the interface is returning a generic type T This does not
services.AddScoped<IApiKeysService>();
Error Using the generic type blah requires 1 type Arguments
Or this
services.AddScoped<IApiKeysService<T>>();
I get the following compile error
type or namespace T cannot be found
How do I register it in Startup.cs?
NOTE: I cannot use a concrete type because the interface member is a base class with generic method.
Interfaces
You cannot instantiate an interface, so it is not possible to register an interface without an implementation type (whether generic or not).
However, it is possible to register types as the concrete type instead of an interface.
services.AddScoped(typeof(Foo<>));
Which is specified in the constructor as:
public MyService(Foo<ClosingType> foo)
{
// implementation
}
Open Generics
Open generics are always specified the same way in .NET: typeof(SomeGenericType<>).
services.AddScoped(typeof(IFoo<>), typeof(Foo<>));
If there are multiple open generic parameters, you would specify how many using commas. For example, with 2 open generic parameters:
services.AddScoped(typeof(IFoo<,>), typeof(Foo<,>));

Kotlin enums fail to cast after deserialize

I've got a data class with a map in it. One of the values stored in the map is a Kotlin enum class.
public enum SecurityRole
{
User,
Admin
}
It serialized and deserialized correctly it seems, but when I try to pull it out of the map and cast it back to the proper type, it excepts with:
com.fff.security.SecurityRole cannot be cast to com.fff.security.SecurityRole
Looking at it in the debugger it looks totally fine, all the data is there, it just makes no sense! I've tried using Java serialization with it, FST's serialization, Klaxon JSON serialization, they all fail to deserialize this thing in a way that's castable afterward, what am i doing wrong!
This happens when SecurityRole is loaded with two different ClassLoaders. Even if they are the same class, the Class object which was loaded is not the same instance. Most of the cases the solution is to instantiate the ClassLoader itself with having the other ClassLoader as parent.

Using Jackson to Serialize/Deserialize a Polymorphic Map with Properties

I am currently using Guava's ForwardingMap as a base class and have numerous types that extend it. I need to maintain the Map type because instances need to be treated as such in consumers. So, even though internally the ForwardingMap using composition the external interface still has to be a map.
As a map, deserializing just key-value properties using #JsonAnyGetter and #JsonAnySetter work fine but, I also need to take into account custom properties, using #JsonProperty, which may also be a part of the instance as well.
So, when serializing or deserializing I want all of the entries and any custom properties which may be a part of the extended class.
I have looked at numerous types of solutions, such as using the Shape.OBJECT and apply interfaces, but none of them seem to work properly for me. I believe I need to create a custom deserializer/serializer to handle the bean + map processing in Jackson but cannot find any examples as to how to do this.
These links help to explain what I am trying to do with no luck:
http://www.cowtowncoder.com/blog/archives/2013/10/entry_482.html
How to serialize with Jackson a java.util.Map based class (cannot change base of ForwardingMap)
Jackson - ignore Map superclass when serializing (cannot change base because it needs to remain a Map)
Ideally, I would like an example or pointer of how to serialize and deserialize an instance that extends ForwardingMap using #JsonAnySetter and #JsonAnyGetter and has custom properties using #JsonProperty as well.
I would want my output to look like
"modules": {
"MyModel": { <-- extends ForwardingMap<>
"domain": "typeinfo",
"property":"hello", <-- comes from #JsonProperty
"another": "GoodBye", <-- comes from #JsonAnyGetter
"another2": 50 <-- comes from #JsonAnyGetter
}
}

WCF MessageContract Inheritance

I am fairly new to WCF and just have a question on how to correctly get MessageContract inheritance working. A simplified version of my setup is as follows - a "base" message type, and then another "test" message which inherits from it.
[MessageContract]
public abstract class BaseMessage
{ }
[MessageContract]
public class TestMessage : BaseMessage
{ }
I then have an asynchronous OperationContract on a ServiceContract defined as:
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginFindRequest(BaseMessage request, AsyncCallback callback, object asyncState);
The problem that I am getting is when calling the BeginFindRequest method, and passing in a TestMessage instance for the request parameter, the WCF framework is deserialising the TestMessage instance to BaseMessage on the service/server side. As this is defined as an abstract class, it results in the following error:
"The message cannot be deserialized
into MessageContract type BaseMessage
since it does not have a default
(parameterless) constructor."
From the limited information that I can find on MessageContract inheritance, it seems that it should just work.
So my question is - what am I missing in order to get this to work; or should I perhaps rather define a seperate OperationContract on the ServiceContract specifically for that type - the downside being that I could end up with many additional OperationContracts?
In the end I found this blog post which hit the nail on the head -
Unfortunately the way that contracts
are expressed in WCF makes is very
easy to forget what their purpose is:
to define the messages send to the
operation and being sent back from the
operation. In reality you have to
think “how would I express this data
in XML?”. XML doesn’t support
inheritance so whatever you put in the
contract is going to have to have some
way of mapping to XML. The data
contracts used to define the messages
are simply a .NET typed convenience
for generating the XML for the data
you want to pass – if you view them
any other way you are destined for a
world of pain. So think about the data
you want to pass, not how it may
happen to be represented in your
business layer and design your
DataContracts accordingly.
http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,a3775eb1-b441-43ad-b9f1-e4aaba404235.aspx
So I will be refactoring to provide an additional method with an explicit contract type. This will also allow me to clean up the service implementation by removing all the type checking.
Thanks for the assistance.
OK, first question is: why are you really using Message contracts? Do you really have a need for that??
Typically, message contracts are only ever used when you need to tightly control the layout of your SOAP message, e.g. to satisfy a legacy system you need to call which requires specific headers and such.
A "normal" WCF call should hardly ever need to use a message contract.
You define your service calls (the methods on your service) using [ServiceContract], and the data structures being passed around as [DataContract]. If you have a DataContract, you have more options as to how to deal with inheritance / polymorphism in your service (more than with the message contract construct).
Marc
Is that possible to change BaseMessage so that it is concrete class with parameterless constructor?
The error message tells that there is no way to initialize the object of type BaseMessage because it is abstract.
The error simply wants you to have a default empty contructor that it can use. However, I agree with marc_s; in the projects I've worked on I've rarely used message contract, the only case I can remember was as part of a file transfer service where file chunks were passed in messasges.
Try decorating your [ServiceContract] with the KnownType attribute. Since TestMessage is not 'visible' from a public operation, this helps the plumbing know how to treat it when it sees it.
If this should allow the [DataContract] to be serialized as a TestMessage your still likely to need to handle multiple messages differently via 'is a' or some other casting.
Well I had declared this default (parameterless) constructor for sure, but that was not still working fine for me, for me the issue was, the access modifier was protected while it should be public:
public constructor() { }
^^^^