Typescript : JSON to Typescript Object - json

I just want to deserialise the JSON I am getting it from my server API.
For most of the solutions mentioned out there (without using any third party library), it looks like the problem is solved.
For example, the most up-voted answer on this SO thread : HERE
But the caveat which I observe is that the behaviour is not what I am expecting, when there is some extra property in json response which I am not expecting.
Let's take an example :
class Person {
id: number;
name: string;
}
let jsonString = `{
"id": 1,
"name": "sumit",
"age": 23
}`;
let person: Person = Object.assign(new Person(), JSON.parse(jsonString));
console.log(Object.keys(person));
console.log(JSON.stringify(person));
So when I work with "person" object later on, I expect that it contains only those properties which are mentioned in Person class, after all "person" is of type "Person".
But surprisingly on serializing person, it contains those extra properties which are not in Person class. Here "age"
Output for the above is :
["id", "name", "age"]
{"id":1,"name":"sumit","age":23}
Now, how do I ensure that after the Deserialization, object "person" is not having any extra properties.

If you use JSON.Parse from JSON to Person object, the prototype of the Person object is not created. Which means, if you've added a getter on the Person object, for instance, you cannot access it, because it is not available on the JSON. What you need to do create a new Person Object and map each of the properties in your JSON.
There are libraries for serializing/deserializing of JSON to Object (Person class). Checkout below library for your reference.
https://www.npmjs.com/package/#peerlancers/json-serialization

The other answers are correct. You should consider using 3-d party libraries for proper typescript class-json serialzation such as https://www.npmjs.com/package/ts-jackson

Related

Any way for Jackson JSON to remove wrapper for array/collection?

Working from an existing API SDK and I'm trying to get rid of a large number of wrapper classes, which are there (mostly) to provide an object wrapper node.
eg: ProductRoot is a class, with a single property named product, of type Product (which is the actual class). As the API returns JSON that looks like this for a Product:
"product": {
"id": 632910392,
"title": "IPod Nano - 8GB",
....
I can remove the wrapper class ProductRoot, and instead annotate the Product class like this:
#JsonTypeName(value = "product")
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
And it works just fine.
HOWEVER, when the API is returning an array of products, it passes in JSON that looks like this:
{
"products": [
{
"id": 632910392,
"title": "IPod Nano - 8GB",
"body_html": "
Note the lack of a "product" node. So my code blows up with this error:
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id ‘id’ as a subtype of com.test.api.rest.model.Product: known type ids = [product] (for POJO property ‘products’)
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 15] (through reference chain: com.test.api.rest.model.ProductsRoot[“products”]->java.util.ArrayList[0])
Where ProductsRoot is a collection wrapper class, like so:
private List<Product> products = new LinkedList<Product>();
So I THINK the issue is that when it's a single Product, I need the deserializer/serializer to wrap with a "product" node. However, when it's an array/collection of products, I need each product to be unwrapped, or not wrapped.
So far I haven't figured out how to do this using annotations or configuration. I think I could probably do it using a custom deserializer and custom serializer, but would need to do a fair bit of magic for it to work correctly, for all relevant types, etc... And I'm not sure that ends up being a lot better/clearer than the current wrapper class approach:(
Any suggestions or solutions? Thanks!

Kotlin - Array property in data class error

I'm modelling some JSON - and using the following lines
data class Metadata(
val id: String,
val creators: Array<CreatorsModel>
)
along with:
data class CreatorsModel (
val role: String,
val name: String
)
However keep seeing the error: Array property in data class error.
Any ideas why this is?
FYI, the JSON looks like:
{
"id": "123",
"creators": [{
"role": "Author",
"name": "Marie"
}
]
}
In Kotlin you should aim to use List instead of Array where possible. Array has some JVM implications, and although the compiler will let you, the IDE may prompt you to override equals and hashcode manually. Using List will make things much simpler.
You can find out more about the difference here: Difference between List and Array types in Kotlin

How to properly use JSON.parse in kotlinjs with enums?

During my fresh adventures with kotlin-react I hit a hard stop when trying to parse some data from my backend which contains enum values.
Spring-Boot sends the object in JSON form like this:
{
"id": 1,
"username": "Johnny",
"role": "CLIENT"
}
role in this case is the enum value and can have the two values CLIENT and LECTURER. If I were to parse this with a java library or let this be handled by Spring-Boot, role would be parsed to the corresponding enum value.
With kotlin-js' JSON.parse, that wouldn't work and I would have a simple string value in there.
After some testing, I came up with this snippet
val json = """{
"id": 1,
"username": "Johnny",
"role": "CLIENT",
}"""
val member: Member = JSON.parse(json) { key: String, value: Any? ->
if (key == "role") Member.Role.valueOf(value.toString())
else value
}
in which I manually have to define the conversion from the string value to the enum.
Is there something I am missing that would simplify this behaviour?
(I am not referring to using ids for the JSON and the looking those up, etc. I am curious about some method in Kotlin-JS)
I have the assumption there is not because the "original" JSON.parse in JS doesn't do this and Kotlin does not add any additional stuff in there but I still have hope!
As far as I know, no.
The problem
Kotlin.JS produces an incredibly weird type situation when deserializing using the embedded JSON class, which actually is a mirror for JavaScript's JSON class. While I haven't done much JavaScript, its type handling is near non-existent. Only manual throws can enforce it, so JSON.parse doesn't care if it returns a SomeCustomObject or a newly created object with the exact same fields.
As an example of that, if you have two different classes with the same field names (no inheritance), and have a function that accepts a variable, it doesn't care which of those (or a third for that matter) it receives as long as the variables it tries accessing on the class exists.
The type issues manifest themselves into Kotlin. Now wrapping it back to Kotlin, consider this code:
val json = """{
"x": 1, "y": "yes", "z": {
"x": 42, "y": 314159, "z": 444
}
}""".trimIndent()
data class SomeClass(val x: Int, val y: String, val z: Struct)
data class Struct(val x: Int, val y: Int, val z: Int)
fun main(args: Array<String>) {
val someInstance = JSON.parse<SomeClass>(json)
if(someInstance.z::class != Struct::class) {
println("Incompatible types: Required ${Struct::class}, found ${someInstance.z::class}");
}
}
What would you expect this to print? The natural would be to expect a Struct. The type is also explicitly declared
Unfortunately, that is not the case. Instead, it prints:
Incompatible types: Required class Struct, found class Any
The point
The embedded JSON de/serializer isn't good with types. You might be able to fix this by using a different serializing library, but I'll avoid turning this into a "use [this] library".
Essentially, JSON.parse fails to parse objects as expected. If you entirely remove the arguments and try a raw JSON.parse(json); on the JSON in your question, you'll get a role that is a String and not a Role, which you might expect. And with JSON.parse doing no type conversion what so ever, that means you have two options: using a library, or using your approach.
Your approach will unfortunately get complicated if you have nested objects, but with the types being changed, the only option you appear to have left is explicitly parsing the objects manually.
TL;DR: your approach is fine.

Json4s: keep unknown fields in a map when deserialising

I am trying to parse the response given by a HTTP endpoint using json4s in scala. The Json returned could have many number of fields (They are all documented and defined, but there are a lot of them, and they are subject to change.) I don't need to reference many of these fields, only pass them on to some other service to deal with.
I want to take the fields I need, and deserialise the rest to a map. The class needs to be serialised correctly also.
e.g. JSON response from endpoint:
{
"name": "value",
"unknown_field": "unknown",
"unknown_array": ["one", "two", "three"],
...
...
}
e.g. case class used in code:
case class TestResponse(name: String, otherFields: Map[String, Any])
Is there a simple solution for this?
I have made an attempt to implement a custom Serialiser for this, but have not had much luck as yet. Seems like this would be a common enough requirement. Is there a way to do this OOTB with json4s?
Cheers
Current attempt at customer serialiser:
private object TestResponseDeserializer extends CustomSerializer[TestResponse](_ => ( {
case JObject(JField("name_one", JString(name)) :: rest) => TestType1Response(name, rest.toMap)
case JObject(JField("name_two", JString(name)) :: rest) => TestType2Response(name, rest.toMap)
}, {
case testType1: TestType1Response=>
JObject(JField("name_one", JString(testType1.name)))
case testType2: TestType2Response=> JObject(JField("name_two", JString(testType2.name)))
}))
I was able to solve this using the custom serialiser in the original question. It didn't work for me due to an unrelated issue.

Changing an immutable object F#

I think the title of this is wrong but can't create a title that reflects, in the abstract, what I want to achieve.
I am writing a function which calls a service and retrieves data as a JSON string. The function parses the string with a JSON type provider. Under certain conditions I want to amend properties on that JSON object and then return the string of the amended object. So if the response from the call was
{"property1" : "value1","property2" : "value2", "property3": "value3" }
I want to change property3 to a new value and then return the JSON string.
If the JsonProvider was mutable this would be an exercise like:
type JsonResponse =
JsonProvider<""" {"property1" : "value1",
"property2" : "value2",
"property3": "value3" } """>
let jsonResponse = JsonResponse.Parse(response)
jsonResponse.Property3 <- "new value"
jsonResponse.ToString()
However, this does not work as the property cannot be set. I am trying to ascertain the best way to resolve this. I am quite happy to initialise a new object based on the original response but with amended parameters but I am not sure if there is an easy way to achieve this.
For reference, the JSON object is much more involved than the flat example given and contains a deep hierarchy.
Yes, you would need to create a new object, changing the bits you want and using the existing object's values for the rest. We added write APIs for both the XML and JSON type providers a while back. You will notice the types representing your JSON have constructors on them. You can see an example of this in use at the bottom of this link