When trying to deserialize a record member of type Header option returned from a JSON string, I get the following exception:
The data contract type
'Microsoft.FSharp.Core.FSharpOption`1[[MyWeb.Controllers.Header,
MyWeb.Controllers, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null]]' cannot be deserialized because the required
data member 'value' was not found.
I'm serializing/deserializing a Message record:
[<DataContract>]
type Header =
{ [<DataMember>] mutable ID : int
[<DataMember>] mutable Description : string }
[<DataContract>]
type Message =
{ [<DataMember>] mutable ID : int
[<DataMember>] mutable Header : Header option
[<DataMember>] mutable SenderID : string
[<DataMember>] mutable ReceiverID : string }
The code I use to deserialize the JSON:
let deserializeJson<'a> (s:string) =
use ms = new MemoryStream(ASCIIEncoding.ASCII.GetBytes s)
let serialize = DataContractJsonSerializer(typeof<'a>)
serialize.ReadObject ms :?> 'a
And the actual raw JSON result:
"Message":
{
"ID":13,
"Header": { "Value":{"ID":21,"Description":"some"}},
"SenderID":"312345332423",
"ReceiverID":"16564543423"
}
The question: how do I deserialize a 'a option?
Update
ASP.NET MVC uses JavaScriptSerializer by default to serialize objects and I'm using DataContractJsonSerializer to deserialize.
For some reason it seems DataContractJsonSerializer can't read the JSON string unless the Value property for the option is in lowercase (as pointed out by #svick). A dirty fix would be to replace "Value" with "value" in the returned JSON string, but I've chosen to go with Roberts' suggestion.
If you were to hop over to using json.net (a.k.a Newtonsoft.Json) instead of the json serializer that comes with the .NET framework, then you could use the option serializer I built to allow me to work more effectively with ravendb. Should just be a matter of registering the convert with the serializer and calling Deserialize.
Related
I have the following Json object:
{
"List":[
"string1",
"string2",
"string3",
"..."
]
}
This deserializes into a JsonArray containing JsonLiterals.
When deserializing this and trying to iterate over it I get an error:
java.lang.ClassCastException: kotlinx.serialization.json.JsonLiteral cannot be cast to java.lang.String
What would be the standard way to achieve something like that?
First, make sure that you are using the latest version of kotlinx.serialization.
A comment in the implementation of JsonLiteral states that
JsonLiteral is deprecated for public use and no longer available. Please use JsonPrimitive instead
JsonPrimitive has the isString method to check if it is a string and you can then access it using its content method.
I'm trying to deserialize object from string, but get "no String-argument constructor/factory method to deserialize from String value ('test')" exception.
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
class KotlinJacksonTest : FunSpec({
val mapper = jacksonObjectMapper()
test("deserialize as string") {
class Ref #JsonCreator constructor(#JsonValue val name: String)
class Root(val ref: Ref)
val root = mapper.readValue<Root>(""" { "ref": "test"} """)
root.ref.name shouldBe "test"
}
})
What I need is to make jackson serialize my object as if it was a string.
But it constantly fails with the following error:
Cannot construct instance of `KotlinJacksonTest$1$1$Ref` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('test')
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `KotlinJacksonTest$1$1$Ref` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('test')
at [Source: (String)" { "ref": "test"} "; line: 1, column: 11] (through reference chain: KotlinJacksonTest$1$1$Root["ref"])
What I'm doing wrong? I can clearly remember this worked for me in java when I needed to do same but with map instead of string.
Ok, after reading some documentation on JsonCreator annoation turns out that it must be like this:
class Ref #JsonCreator(mode = JsonCreator.Mode.DELEGATING) constructor(#JsonValue val name: String)
The JsonCreator.Mode.DELEGATING makes it work like I wanted.
UPD: There is even a possibility to make it shorter - custom jackson annotation.
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.CONSTRUCTOR)
#JacksonAnnotationsInside
#JsonCreator(mode = JsonCreator.Mode.DELEGATING)
annotation class JsonDelegating
And usage:
class Ref #JsonDelegating constructor(#JsonValue val name: String)
You can avoid having to annotate the constructor of your data class completely by just targeting the #JsonValue annotation on your property to its getter method, like so:
class Ref(#get:JsonValue val name: String)
Notably #field:JsonValue does not work in this case either.
Why your original code, nor the field targeting, do not work as expected, I couldn't tell you.
I have a question related to Jackson and polymorphism: is there a way to deserialize a JSON string without specifying a type?
Assuming I don't own this message (e.g., external API) and I have two separate messages that come in at separate times:
{
"responseCode": 200
"responseMessage": "You did something successfully"
}
{
"errorCode": 401
"errorDescription": "Permission denied"
}
And I want to deserialize this message with some data classes that I created based on these messages through polymorphism (see abstract class in next code block):
data class MyDataClass(
val responseCode: Int,
val responseMessage: String
): MyAbstractClass()
data class MyOtherDataClass(
val errorCode: Int,
val errorDescription: String
): MyAbstractClass()
And I am resolving these messages through a function that will use the Jackson Object Mapper to deserialize the stringified JSON payload:
#JsonSubTypes(
JsonSubTypes.Type(value = MyDataClass::class),
JsonSubTypes.Type(value = MyOtherDataClass::class)
)
#JsonIgnoreProperties(ignoreProperties = true)
abstract class MyAbstractClass
fun receiveMessage(message: String) {
val convertedMessage = jacksonObjectMapper().readValue<MyAbstractClass>(message)
log.info(convertedMessage)
/* prints either:
MyDataClass(responseCode=200, responseMessage=You did something successfully)
OR
MyOtherDataClass(errorCode=401, errorDescription=Permission denied)
*/
}
But since I haven't described how to identify the data class (using #JsonTypeInfo), it fails.
To repeat, I am curious if there is a way that I can deserialize the incoming message to one of my polymorphic types without having to specify the #JsonTypeInfo. Or if I must describe the #JsonTypeInfo, how would I do this with no similarities between the two child classes of MyAbstractClass?
I would write a custom deserializer which takes it as a generic JSONObject or the like. Then I'd check if a differentiating key exists. For example:
// pseudocode
when (json: JSONObject) {
hasKey("responseCode") -> // deserialize as MyDataClass
hasKey("errorCode") -> // deserialize as MyOtherDataClass
}
Hi I'm forcing problem with probably deserializer. So I've got response object like:
Map<Pair<Int, String>, List<DTO>>
But in swagger model it's visible as:
{ < * >: [...] }
Ofc I have jackson deserializer based on Deserializing non-string map keys with Jackson
Update :
I'm expecting to have instead of < * >. Or some nammed by me values like id and name. I've also tried to set Object in place of key instead of pair but nothing happens.
Map<Keyobj, List<DTO>>
data class Keyobj(val int: Int, val string: String)
I have the following Kotlin data class:
data class TestObject(
val boolField: Boolean,
val stringField: String,
val nullBoolField: Boolean = true,
val nullStringField: String = "default",
val notThereBoolField: Boolean = true,
val notThereStringField: String = "not there"
)
I am then attempting to deserialize some JSON into this class using Jackson v2.9.4 with the Jackson Kotlin plugin v2.9.4.1. The test JSON is as follows:
{
"boolField": true,
"stringField": "string",
"nullBoolField": null,
"nullStringField": null
}
The first two and the last two fields deserialize successfully - with the values from the JSON and the default values from the Kotlin data class, as appropriate. However, when the middle pair of fields are present, the deserialization fails with:
Instantiation of [simple type, class com.example.TestObject] value
failed for JSON property nullStringField due to missing (therefore
NULL) value for creator parameter nullStringField which is a
non-nullable type
I know I could solve the problem by changing the types of nullBoolField and nullStringField to Boolean? and String? respectively, but since default values are known I would rather not have to push the handling of null further down into the code by doing that.
The question is: is there any way of configuring Jackson to use the Kotlin data class default values for non-nullable properties when the property is present in the JSON but set to null?
You could try first to filter null values from json and after to deserialize it to Kotlin object.
Or you may to try add feature to kotlin-jackson module, with adding a new feature parameter, which will enable a null ignoring from json parameters and use default values.
You may do this by modify this line (if I'm not mistaken)
If you don't have any value and field is non-nullable then you should not pass it in request body:
{
"boolField": true,
"stringField": "string"
}
This results in following object, as expected:
{
"boolField": true,
"stringField": "string",
"nullBoolField": true,
"nullStringField": "default",
"notThereBoolField": true,
"notThereStringField": "not there"
}