How to add another object in JSON with Ktor - json

I'm creating API server with Ktor and using exposed SQL.
I have two tables like below:
/// Performance Table
#kotlinx.serialization.Serializable
data class PerformanceDAO(val performanceId: String, val title: String, val actor: String)
object Performances : Table() {
val performanceId: Column<String> = varchar("performanceId", 20)
val title: Column<String> = varchar("title", 50)
override val primaryKey = PrimaryKey(performanceId)
}
//Actor Table
object Actors: Table() {
val performanceId: Column<String> = reference("performanceId", Performances.performanceId)
val name: Column<String> = varchar("name", 10)
}
and I'm trying to get data this way:
class PerformanceDAOImpl : PerformanceDAOFacade {
private fun resultRowToPerformance(row: ResultRow) = PerformanceDAO(
performanceId = row[Performances.performanceId],
title = row[Performances.title],
actor = row[Actors.name],
)
override suspend fun allPerformances(): List<PerformanceDAO> = dbQuery {
Actors.leftJoin(Performances)
.slice(Actors.name, Performances.title, Performances.performanceId)
.selectAll()
.groupBy(Performances.title).map(::resultRowToPerformance)
}
}
and then I got only one actors without a key.
{
"performanceId": "PF13234",
"title": "Harry footer",
"actor": [
"John"
]
},
but I want get data like this
{
"performanceId": "PF13234",
"title": "Harry footer",
"actor": [
{
"name: "John",
"image": "/upload/images/image.jpg"
},
{
"name: "Harry",
"image": "/upload/images/image.jpg"
},
]
},
I want to know how to make sub object in JSON with Exposed SQL!

You have to create a separate data class ActorDTO and map it in your code by making subquery. Also, I would advice to make Exposed Entities and then map them to your DAO, it can be much simpler but will add some boilerplate code.
Please check documentation

Related

Tornadofx REST client

I have followed an example shown here
link
And i got the hang of it, i managed to create my own "Employee" entity and i found some dummy api data online to play with.
like this Problem is, the tornadofx throws null pointer error, and i think its because the rest response sends something like this
{
"status": "success",
"data": [
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},
but when i use mocky and provide JUST the json part
[
{
"id": "1",
"employee_name": "Tiger Nixon",
"employee_salary": "320800",
"employee_age": "61",
"profile_image": ""
},...]
it all works fine.
I think those additional fields "status" and "success" in response confuse the rest client of tornadofx, and i cant manage to get it to work, is there anyway to tell client to ignore every other fields besides those of json data.
All links are functional, so you can try yourself.
full working example
package com.example.demo.view
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import javafx.scene.layout.BorderPane
import tornadofx.*
import javax.json.JsonObject
class Employee (id:Int?=null , name: String? = null, age: Int?=null): JsonModel {
val idProperty = SimpleIntegerProperty(this, "id")
var id by idProperty
val ageProperty = SimpleIntegerProperty(this, "age")
var age by ageProperty
val employeeNameProperty = SimpleStringProperty(this, "name", name)
var name by employeeNameProperty
override fun updateModel(json: JsonObject) {
with(json) {
id = int("id")!!
age = int("employee_age")!!
name = string("employee_name")
}
}
override fun toJSON(json: JsonBuilder) {
with(json) {
add("id", id)
add("employee_name", name)
add("employee_age", age)
}
}
}
class PersonEditor : View("Person Editor") {
override val root = BorderPane()
val api : Rest by inject()
var persons = listOf(Employee(1,"John", 44), Employee(2,"Jay", 33)).observable()
val model = PersonModel(Employee())
init {
api.baseURI = "https://run.mocky.io/v3/"
val response = api.get("f17509ba-2d12-4c56-b441-69ab23302e43")
println(response.list())
println(response.list().toModel<Employee>()[0].name)
// print( p.get(1))
with(root) {
center {
tableview(response.list().toModel<Employee>()) {
column("Id", Employee::idProperty)
column("Name", Employee::employeeNameProperty)
column("Age", Employee::ageProperty)
// Update the person inside the view model on selection change
model.rebindOnChange(this) { selectedPerson ->
item = selectedPerson ?: Employee()
}
}
}
right {
form {
fieldset("Edit person") {
field("Id") {
textfield(model.id)
}
field("Name") {
textfield(model.name)
}
field("Age") {
textfield(model.age)
}
button("Save") {
enableWhen(model.dirty)
action {
save()
}
}
button("Reset").action {
model.rollback()
}
}
}
}
}
}
private fun save() {
// Flush changes from the text fields into the model
model.commit()
// The edited person is contained in the model
val person = model.item
// A real application would persist the person here
println("Saving ${person.employeeNameProperty} / ${person.ageProperty}")
}
}
class PersonModel(person: Employee) : ItemViewModel<Employee>(person) {
val id = bind(Employee::idProperty)
val name = bind(Employee::employeeNameProperty)
val age = bind(Employee::ageProperty)
}
if you replace base url and send request to http://dummy.restapiexample.com/api/v1/employees you will get an error that i am talking about
Your call to mocky returns a list, so .list() works fine. Your call to restapiexample, however, returns an object, not a list, so .list() won't do what you expect. You can probably use something like this, though I haven't tested it:
response.one().getJsonArray("data").toModel<Employee>()[0].name)
Further explanation:
If you're not familiar with the structure of JSON, check out the diagrams on the JSON homepage.
TornadoFX has two convenience functions for working with JSON returns: .list() and .one(). The .list() function will check if the result is a JsonArray. If so, it simply returns it. If it is instead a JsonObject, it wraps that object in a list and returns the new list.
In your case, since restapiexample is returning an object, the result of your call to .list() is a JsonArray with a single object. It looks something like this:
[
{
"status": "success",
"data": [...]
}
]
Obviously that single object cannot be converted to an Employee, so dereferencing anything off of it will result in a NullPointerException.
The .one() function on the other hand will check if the response is a JsonObject. If it is, it simply returns the object. If, however, the response is a JsonArray, it will take the first item from the array and return that item.

JSON serialization of optional values with FsPickler

Is it possible to serialize optional values in F# using FsPickler such that:
when the value is Some(), the value contained is serialized
and when it is None, it does not get serialized at all?
With the following example code:
type Person = { name: string; age: int option }
let data = [
{ name = "Helena"; age = Some(24) };
{ name = "Peter"; age = None }
]
let jsonSerializer = FsPickler.CreateJsonSerializer(true, true)
let streamWriter = new StreamWriter(#"C:\output.json")
let out = jsonSerializer.SerializeSequence(streamWriter, data)
the output.json file contains the following JSON:
[
{
"name": "Helena",
"age": {
"Some": 24
}
},
{
"name": "Peter",
"age": null
}
]
But I would like the contents of JSON file to look like this instead:
[
{
"name": "Helena",
"age": 24
},
{
"name": "Peter"
}
]
I am using FsPickler.Json v3.2.0 and Newtonsoft.Json v9.0.1.
UPDATE (January 11, 2017): Using the script in the gist linked by Stuart in the answer below, I got it working like this:
let obj = { name = "Peter"; age = None }
let stringWriter = new StringWriter()
let jsonSerializer = new JsonSerializer()
jsonSerializer.Converters.Add(new IdiomaticDuConverter())
jsonSerializer.NullValueHandling <- NullValueHandling.Ignore
jsonSerializer.Serialize(stringWriter, obj)
Using Newtonsoft.Json you can use the gist here (credit: Isaac Abraham) to give you the behaviour you are after.
It's funny, just moments before you posted this, I was looking to see if the same thing exists within FsPickler.Json, but came to no conclusions. You could use TypeShape which is used in FsPickler to make the gist cleaner, but not sure if FsPickler.Json can do this for you out of the box.
I'm the author FsPickler, so thought I'd repost a response I gave in a similar issue.
No, managing the shape of the serialization formats is beyond the design goals of this library. While you could use pickler combinators to influence how serialized types look like, this will only take you so far.
With FSharp.Json library it would work just well:
open FSharp.Json
type Person = { name: string; age: int option }
let data = [
{ name = "Helena"; age = Some(24) };
{ name = "Peter"; age = None }
]
let json = Json.serialize data
This produces following JSON:
[
{ "name": "Helena", "age": 24 },
{ "name": "Peter", "age": null }
]
How option None is serialized is configurable. Here's how to omit the option member that has None value, pay attention to config:
let config = JsonConfig.create(serializeNone=SerializeNone.Omit)
let json = Json.serializeEx config data
Disclosure: I'm author of FSharp.Json library.

UWP - From Json string to Structure (Classes)

I receive a JSon string from WS. It's so long that I can't use Json2charp to parse it and receive the structurated class.
I want to parse the string with a command. How is it possible?
I don't know the classes so I can't use a command like:
Dim result = JsonConvert.DeserializeObject(Of MyClass.RootObject)(String_From_File)
Is it possible from the string to obtain the class without using json2charp site ?
For example, in vs.net if on the variable 'string_from_file' I choose 'Json Visualizer' and see all classes and data in correct mode.
How can I obtain the same in my code ?
I have installed Newtonsoft.json
If you cannot use the json to class mappers like NewtonSoft.Json. You can use the Windows.Data.Json api. It let you parse and extract the data you want from your JSON string.
JsonValue jsonValue = JsonValue.Parse("{\"Width\": 800, \"Height\": 600, \"Title\": \"View from 15th Floor\", \"IDs\": [116, 943, 234, 38793]}");
double width = jsonValue.GetObject().GetNamedNumber("Width");
double height = jsonValue.GetObject().GetNamedNumber("Height");
string title = jsonValue.GetObject().GetNamedString("Title");
JsonArray ids = jsonValue.GetObject().GetNamedArray("IDs");
You can find a sample in the Windows Universal Sample GitHub.
A complex object parsing is shown here. I've extracted the most relevant parts here. The JSON string is provided to the User constructor which is extracting what it needs and then delegating the parsing to the nested School constructor.
{
"id": "1146217767",
"phone": null,
"name": "Satya Nadella",
"education": [
{
"school": {
"id": "204165836287254",
"name": "Contoso High School"
},
"type": "High School"
},
{
"school": {
"id": "116138758396662",
"name": "Contoso University"
},
"type": "College"
}
],
"timezone": -8,
"verified": true
}
This JSON fragment is parsed with this code:
public User(string jsonString) : this()
{
JsonObject jsonObject = JsonObject.Parse(jsonString);
Id = jsonObject.GetNamedString(idKey, "");
IJsonValue phoneJsonValue = jsonObject.GetNamedValue(phoneKey);
if (phoneJsonValue.ValueType == JsonValueType.Null)
{
Phone = null;
}
else
{
Phone = phoneJsonValue.GetString();
}
Name = jsonObject.GetNamedString(nameKey, "");
Timezone = jsonObject.GetNamedNumber(timezoneKey, 0);
Verified = jsonObject.GetNamedBoolean(verifiedKey, false);
foreach (IJsonValue jsonValue in jsonObject.GetNamedArray(educationKey, new JsonArray()))
{
if (jsonValue.ValueType == JsonValueType.Object)
{
Education.Add(new School(jsonValue.GetObject()));
}
}
}
public School(JsonObject jsonObject)
{
JsonObject schoolObject = jsonObject.GetNamedObject(schoolKey, null);
if (schoolObject != null)
{
Id = schoolObject.GetNamedString(idKey, "");
Name = schoolObject.GetNamedString(nameKey, "");
}
Type = jsonObject.GetNamedString(typeKey);
}
If you cannot use the automatic mapping from NewtonSoft.Json, you have no other way than doing it yourself.
Is not so simple.
The Json i received is very complicated and have many class
So i can't use
double width = jsonValue.GetObject().GetNamedNumber("Width");
Inside class i have more ...

Serializing a map of custom types to JSON using Scala and Play Framework

I created a Map of custom types by using information found here http://www.blaenkdenum.com/notes/play/:
def getAllProductsWithStockItems: Map[Product, List[StockItem]] = {
DB.withConnection { implicit connection =>
val sql = SQL("select p.*, s.*" +
"from products p" +
"inner join stock_items s on (p.id = s.product_id)")
val results: List[(Product, StockItem)] = sql.as(productStockItemParser *)
results.groupBy { _._1 }.mapValues { _.map { _._2 } }
}
}
I need to serialize/deserialize Map[Product, List[StockItem]] into JSON, but I don't really know how to do it. I already have working serializers for both custom types (Product and StockItem) which I coded using Play's native JSON library, but I can use another one if it's better. Can someone give me some pointers?
Update: The List/StockItem example is not a good one, but it's just an abstraction. In the end, I have a structure which represents 1 to n relationships.
So, considering each Product has a list of StockItem, I expect the JSON to look like something similar to this:
{
"products": [
{
"id": 1
"productName": "coffee",
"stockitems": [
{
"id":1,
"brandName": "Crazycoffee",
"price": 5
"quantity": 3000
},
{
"id":2,
"brandName": "Badcoffee",
"price": 1
"quantity": 2000
}
]
},
{
"id": 1
"productName": "ice cream",
"stockitems": [
{
"id":1,
"brandName": "Sleazyice",
"price": 2
"quantity": 300
}
]
}
]
}
I haven't tested it, but it is writing from part of my working java code. Using Jackson library (included in play 2) :
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
public static String toJson(Map<Product, List<StockItem>> lst)
{
JsonFactory factory = new JsonFactory();
CharArrayWriter writer = new CharArrayWriter();
try{
JsonGenerator gen = factory.createGenerator(writer);
gen.writeStartObject();
gen.writeFieldName("infos");
gen.writeRaw(":" + listToJson(lst));
gen.writeEndObject();
gen.close();
}catch(Throwable e){
Logger.error("AjaxRequest.generateResponse: " + e.getMessage());
e.printStackTrace();
}
return new String(writer.toCharArray());
}
/**
* Convert a list into a json string.
*/
public static String listToJson(List<?> lst) throws IOException
{
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = new JsonFactory();
CharArrayWriter writer = new CharArrayWriter();
JsonGenerator gen = factory.createGenerator(writer);
gen.writeRaw(mapper.writeValueAsString(lst));
gen.close();
return new String(writer.toCharArray());
}
Jackson library work well, but is so far from being easy to understand. Anyway, with few modifications, this code should work.
You could consider using https://playframework.com/documentation/2.5.x/ScalaJsonAutomated , then you could save some time while not having to write all the Writers and Readers by hand. You could create a "wrapper" entity based on your top level JSON element "products" such as ProductsList (for example). Here is an example of how you could do it:
case class ProductList (
list: Vector[Product]
)
object ProductList {
/**
* Mapping to and from JSON.
*/
implicit val documentFormatter: Format[ProductList] = (
(__ \ "products").format[Vector[Product]]
) (ProductList.apply, unlift(ProductList.unapply))
}

Exclude null values using JSONBuilder in Groovy

Is it possible to create JSON values in Groovy using the default JsonBuilder library to exclude all the null values of an object? Such as what Jackson does in Java by annotating classes to exclude null values.
An example would be:
{
"userId": "25",
"givenName": "John",
"familyName": null,
"created": 1360080426303
}
Which should be printed as:
{
"userId": "25",
"givenName": "John",
"created": 1360080426303
}
Not sure if it's OK for you as my method works on a Map with List properties:
def map = [a:"a",b:"b",c:null,d:["a1","b1","c1",null,[d1:"d1",d2:null]]]
def denull(obj) {
if(obj instanceof Map) {
obj.collectEntries {k, v ->
if(v) [(k): denull(v)] else [:]
}
} else if(obj instanceof List) {
obj.collect { denull(it) }.findAll { it != null }
} else {
obj
}
}
println map
println denull(map)
yields:
[a:a, b:b, c:null, d:[a1, b1, c1, null, [d1:d1, d2:null]]]
[a:a, b:b, d:[a1, b1, c1, [d1:d1]]]
After filter null values out, you then can render the Map as JSON.
I used the Groovy metaClass to workaround this issue, but am not sure it would work in all cases.
I created a Class to hold the required elements, but left out the optional elements that could possibly have a null (or empty) value.
private class User {
def id
def username
}
Then, I added the data to this class. My use case was fairly complex so this is a simplified version just to show an example of what I did:
User a = new User(id: 1, username: 'john')
User b = new User(id: 2, username: 'bob')
def usersList = [a,b]
usersList.each { u ->
if (u.id == 1)
u.metaClass.hobbies = ['fishing','skating']
}
def jsonBuilder = new JsonBuilder([users: usersList])
println jsonBuilder.toPrettyString()
Results:
{
"users": [
{
"id": 1,
"username": "john",
"hobbies": [
"fishing",
"skating"
]
},
{
"id": 2,
"username": "bob"
}
]
}
If you do not need use JSONBuilder you can use com.fasterxml.jackson:
Make object:
private static final ObjectMapper JSON_MAPPER = new ObjectMapper().with {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE)
}
and display your list of maps like that (maps can have any Object inside):
println(JSON_MAPPER.writeValueAsString(listOfMaps))
If you are using Groovy >2.5.0, you can use JsonGenerator. The example below is taken from Groovy's Documentation as of July 2018.
class Person {
String name
String title
int age
String password
Date dob
URL favoriteUrl
}
Person person = new Person(name: 'John', title: null, age: 21, password: 'secret',
dob: Date.parse('yyyy-MM-dd', '1984-12-15'),
favoriteUrl: new URL('http://groovy-lang.org/'))
def generator = new JsonGenerator.Options()
.excludeNulls()
.dateFormat('yyyy#MM')
.excludeFieldsByName('age', 'password')
.excludeFieldsByType(URL)
.build()
assert generator.toJson(person) == '{"dob":"1984#12","name":"John"}'