How to convert data class to map - json

I tried convert to data class to map. (Under the code is just example code)
data class User (
val name : String = "",
val age : Int = 0,
val deviceGroup: MutableSet<DeviceGroup> = mutableSetOf()
)
data class DeviceGroup (
val name : String = "",
val deviceLink : MutableSet<DeviceLink> = mutableSetOf()
)
data class DeviceLink (
val id : Int = 0,
val device : Device
)
data class Device (
val devId : Int = 0,
val name : String = ""
)
fun main (request : HttpServletRequest) {
val currentUser = request.session.getAttribute("user") as User
val data = userRepository.findByName(currentUser.name)
// return currentUser
// result is {name="test", age=17, deviceGroup = [{name="group1"}, {name="group2"}]}
// I want deserialization data class to Map
val response = data.deviceGroup.toMap()
response.deivceGroup.forEach {
// And add new key, pair
it.add(Map<String, MutableSet<Device>>("devices", mutableSetOf()))
// Lastly, I want put in the value
deviceGroupRepository.findByName(it.name).deviceLink.forEach {
it.devices.add(this)
}
}
return response
}
if just return the data value, that's result is "{name="test", age=17, deviceGroup = [{name="group1"}, {name="group2"}]}"
How to convert to data class to Map object and add new key pair?

Use associate to turn a collection into a Map
The Kotlin standard library provides a function called associate which will take a collection of objects and transform them into a map. It takes one argument, which is a function specifying what the keys and values of the map should be.
For example, in your case, you would call it like this:
val response = data.deviceGroup.associate { it.name to it.deviceLink }
It will return a Map<String, MutableSet<DeviceLink>> where the key is the name of the device group and the value is the deviceLink set.
The easiest way to add new values is simply to append them with the + operator.
val response = data.deviceGroup.associate {
it.name to it.deviceLink
} + mapOf("device" to emptySet())
If you need more control than that, you could use .toMutableMap() so new entries can be added using put.
val response = data.deviceGroup.associate {
it.name to it.deviceLink
}.toMutableMap()
response.put("device", emptySet())

Related

Kafka Streams API GroupBy behaviour

I am new in kafka streams and I am trying to aggregate some streaming data into a KTable using groupBy function. The problem is the following:
The produced message is a json msg with the following format:
{ "current_ts": "2019-12-24 13:16:40.316952",
"primary_keys": ["ID"],
"before": null,
"tokens": {"txid":"3.17.2493",
"csn":"64913009"},
"op_type":"I",
"after": { "CODE":"AAAA41",
"STATUS":"COMPLETED",
"ID":24},
"op_ts":"2019-12-24 13:16:40.316941",
"table":"S_ORDER"}
I want to isolate the json field "after" and then create a KTable with "key" = "ID" and value the whole json "after".
Firstly, I created a KStream to isolate the "after" json, and it works fine.
KStream code block: (Don't pay attention to the if statement because "before" and "after" have the same format.)
KStream<String, String> s_order_list = s_order
.mapValues(value -> {
String time;
JSONObject json = new JSONObject(value);
if (json.getString("op_type").equals("I")) {
time = "after";
}else {
time = "before";
}
JSONObject json2 = new JSONObject(json.getJSONObject(time).toString());
return json2.toString();
});
The output, as expected, is the following:
...
null {"CODE":"AAAA48","STATUS":"SUBMITTED","ID":6}
null {"CODE":"AAAA16","STATUS":"COMPLETED","ID":1}
null {"CODE":"AAAA3","STATUS":"SUBMITTED","ID":25}
null {"CODE":"AAAA29","STATUS":"SUBMITTED","ID":23}
...
Afterwards, I implement a KTable to groupBy the "ID" of the json.
KTable code block:
KTable<String, String> s_table = s_order_list
.groupBy((key, value) -> {
JSONObject json = new JSONObject(value);
return json.getString("ID");
});
And there is an error that I want to create KTable<String, String> but I am creating GroupedStream<Object,String>.
Required type: KTable<String,String>
Provided:KGroupedStream<Object,String>
no instance(s) of type variable(s) KR exist so that KGroupedStream<KR, String> conforms to KTable<String, String>
In conclusion, the question is what exactly are KGroupedStreams and how to implement a KTable properly ?
After groupBy processor, you can use a stateful processor, like aggregate or reduce (that processors returns KTable). You can do something like this:
KGroupedStream<String, String> s_table = s_order_list
.groupBy((key, value) ->
new JSONObject(value).getString("ID"),
Grouped.with(
Serdes.String(),
Serdes.String())
);
KTable<String, StringAggregate> aggregateStrings = s_table.aggregate(
(StringAggregate::new),
(key, value, aggregate) -> aggregate.addElement(value));
StringAggregate looks like:
public class StringAggregate {
private List<String> elements = new ArrayList<>();
public StringAggregate addElement(String element){
elements.add(element);
return this;
}
//other methods
}

Pass DataType of the Class

I have the following data class, which stores two values, JSON and dataType:
data class DataTypeItem(
var json : String = "",
var dataType : Class<*> ?= null
)
I have the list defined in the following way:
val dataTypeList = mutableMapOf<String, DataTypeItem>()
dataTypeList.put( "item_1", DataTypeItem( json1, MyDataType::class.java ) )
dataTypeList.put( "item_2", DataTypeItem( json1, List<MyDataType>::class.java ) )
Please note that in one case I'm using the MyDataType as the DataType and in the other List < MyDataType >.
Now I would like to loop through each of the dataTypeList items and parse JSON for the given data type into it's model:
fun init()
{
dataTypeList.forEach {
dataTypeItem ->
val model = Gson().fromJson( dataTypeItem.value.json, dataTypeItem.value.dataType::class.java )
}
}
I'm using the following model:
data class dataTypeItem(
#SerializedName("sqlId")
val sqlId: String,
#SerializedName("name")
val name: String
)
But I keep getting an Runtime exception:
Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?
In addition, in case it's a list, I need to call toList() on Gson().fromJSON(..):
fun init()
{
dataTypeList.forEach {
dataTypeItem ->
val model;
if( dataTypeItem.value.dataType::class.java is Array )
model = Gson().fromJson( dataTypeItem.value.json, dataTypeItem.value.dataType::class.java ).toList()
else
model = Gson().fromJson( dataTypeItem.value.json, dataTypeItem.value.dataType::class.java )
}
}
How can I pass the dataType dynamically and distinguish if it's a List/Array or straight up class? In addition, whenever I try to call toList(), I get an error that it's undefined.
If I specify the class directly, then it's working fine
var model = Gson().fromJson( json, DataTypeItem::class.java )
or
var model = Gson().fromJson( json, Array<DataTypeItem>::class.java )
but I need to be able to specify it dynamically as an argument
This code works fine:
val dataTypeMap = mapOf(
"item_1" to MyDataTypeItem("""{"sqlId" : "1", "name" : "a"}""", MyDataType::class.java),
"item_2" to MyDataTypeItem("""[{"sqlId" : "1", "name" : "a"}, {"sqlId" : "2", "name" : "b"}]""", Array<MyDataType>::class.java)
)
val result = dataTypeMap.map{ Gson().fromJson(it.value.json, it.value.dataType) }
I renamed DataTypeItem to MyDataTypeItem and dataTypeItem to MyDataType.
Why you need to call toList()? If it is really necessary you can do the following instead:
val result = dataTypeMap.map {
if (it.value.dataType?.isArray == true) Gson().fromJson<Array<*>>(it.value.json, it.value.dataType).toList()
else Gson().fromJson(it.value.json, it.value.dataType)
}

json data parsing using retrofit and rxjava2 data display in Textview and TableLayout

JSON Data Parsing Using Retofit2 and Rxjava2. This Data get In ArrayList successfully. its ArrayList Size is Nine but its display only two Record in Table. After Two Record its Kotlin.NullPointerException.
JSON Data:
{"success":1,"salesGst":[{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"2778.75","TaxTotal":"2778.75","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":"CDE","SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"1","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"2203.0","TaxTotal":"2118.5","InvoiceType":"Tax Invoice","CGSTTotal":"52.96","PartyGST":"CDE","SGSTTotal":"52.96","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"VIKAS","ChallanNo":"2","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"6975.0","TaxTotal":"6975.0","InvoiceType":"Retail Invoice","CGSTTotal":"0.0","PartyGST":null,"SGSTTotal":"0.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"2","ChallanDate":"2019-03-13 00:00:00","ChallanAmount":"420.0","TaxTotal":"403.75","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"20.19"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"3","ChallanDate":"2019-03-14 00:00:00","ChallanAmount":"4788.0","TaxTotal":"4560.0","InvoiceType":"Tax Invoice","CGSTTotal":"114.0","PartyGST":"CDE","SGSTTotal":"114.0","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"4","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"241.9","TaxTotal":"230.38","InvoiceType":"Tax Invoice","CGSTTotal":"5.76","PartyGST":"CDE","SGSTTotal":"5.76","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"5","ChallanDate":"2019-03-15 00:00:00","ChallanAmount":"5563.68","TaxTotal":"5101.5","InvoiceType":"Tax Invoice","CGSTTotal":"231.28","PartyGST":"CDE","SGSTTotal":"231.28","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES GJ","ChallanNo":"6","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"13238.0","TaxTotal":"12459.25","InvoiceType":"Tax Invoice","CGSTTotal":"389.29","PartyGST":"CDE","SGSTTotal":"389.29","IGSTTotal":"0.0"},{"Cmp_Name":"ABC","GSTIN":"AAAA","FirmName":"SALES MH","ChallanNo":"7","ChallanDate":"2019-03-16 00:00:00","ChallanAmount":"2074.0","TaxTotal":"1975.0","InvoiceType":"Tax Invoice","CGSTTotal":"0.0","PartyGST":"ABC","SGSTTotal":"0.0","IGSTTotal":"98.75"}]}
Please Guide Me,After Getting How to Show in TableLayout.
In ArrayList Nine Record but in Table show only Two Record another seven record is not display. in third record taxtotal give kotlin.nullpointerException. what missing?
private fun displaySalesGSTData(salesGSt : List<SalesGST>) {
salesGST = SalesGST()
tvSalesCompanyName.setText(salesGSt.get(1).Cmp_Name)
tvGSTIN.setText(salesGSt.get(1).GSTIN)
val rowHeader = TableRow(this#Sales)
rowHeader.setBackgroundColor(Color.parseColor("#c0c0c0"))
rowHeader.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val headerText = arrayOf<String>("Sr.No.", "Invoice Type", "Bill No.", "Bill Date", "Firm Name", "GST NO","TAX Total","CGST","SGST","IGST","Net Amount")
for (c in headerText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_header)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setTextSize(18F)
tv.setPadding(5, 5, 5, 5)
tv.setText(c)
rowHeader.addView(tv)
}
tableMarks.addView(rowHeader)
for (j in 0 until salesGSt.size)
{
/*val jsonObject1 = jsonArray.getJSONObject(j)
val date = jsonObject1.getString("ExamDate")
val inputFormatter1 = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date1 = inputFormatter1.parse(date)
val outputFormatter1 = SimpleDateFormat("dd-MMM-yyyy")
ExamDate = outputFormatter1.format(date1)*/
/* String replaceDate = date.replace("/Date(", "").replace(")/", "");
Long getDate = Long.valueOf(replaceDate);
ExamDate = dateFormat.format(getDate);*/
/*Subject = jsonObject1.getString("subject")
ExamName = jsonObject1.getString("ExamName")
TotalMark = jsonObject1.getLong("TotalMarks")
PassingMark = jsonObject1.getLong("PassingMarks")
Mark = jsonObject1.getLong("Marks")*/
var fName : String = salesGSt.get(j).FirmName!!
var invoice : String = salesGSt.get(j).InvoiceType!!
var bill_no : String = salesGSt.get(j).ChallanNo!!
var bill_date : String = salesGSt.get(j).ChallanDate!!
var gst_no : String = salesGSt.get(j).PartyGST!!
var tax_total : Double = salesGSt.get(j).TaxTotal!!.toDouble()
var cgst : String = salesGSt.get(j).CGSTTotal!!
var igst : String = salesGSt.get(j).IGSTTotal!!
var sgst : String = salesGSt.get(j).SGSTTotal!!
var net_amount : String = salesGSt.get(j).ChallanAmount!!
var sr : Int = j + 1
// dara rows
val row = TableRow(this#Sales)
row.setLayoutParams(TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT))
val colText = arrayOf<String>(sr.toString(),(invoice), bill_no, bill_date, fName, gst_no, tax_total.toString(),cgst,sgst,igst,net_amount)
for (text in colText)
{
val tv = TextView(this#Sales)
tv.setLayoutParams(TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT))
tv.setGravity(Gravity.CENTER)
// tv.setBackgroundResource(R.drawable.table_shape)
tv.setTextSize(18F)
tv.setTextColor(Color.parseColor("#3F51B5"))
tv.setPadding(5, 5, 5, 5)
tv.setText(text)
row.addView(tv)
}
tableMarks.addView(row)
}
}
The 3rd item, salesGst[2], is "PartyGST": null. Your json deserializer library won't handle non-null fields as it's written in Java. I assume you have a data class where PartGST is defined as non-null, yet the deserializer will still parse it as null. Therefore, when you access PartyGST then you will receive a NullPointerException because Kotlin is expecting it to be non-null. This is a good article, which explains in more detail:
I've trusted you! You promised no null pointer exceptions!
A solution to this would be to have two models. The DTO (for JSON response) where all fields are optional and your internal model (used by your app), where you define which fields you want to be optional. Then you can have a mapper to handle the case where a field is null in the DTO, but non-null in your internal model:
// Models for your API response
data class SalesGstsDTO(val gsts: List<GstDTO>)
data class GstDTO(val name: String?, val surname: String?)
// Internal models used by your app
data class SalesGsts(val gsts: List<Gst>)
data class Gst(val name: String, val surname: String?)
class SalesGstDTOMapper {
fun mapToSalesGsts(salesGstsDTO: SalesGstsDTO): SalesGsts {
val gsts = mutableListOf<Gst>()
salesGstsDTO.gsts.map {
val name = it.name ?: return#map // Skips this item. You could handle this how you wish
val surname = it.surname
val gst = Gst(name, surname)
gsts.add(gst)
}
return SalesGsts(gsts)
}
}
This also allows you to decouple your app from the JSON response.

test unit in scala with Json String

I have a method which creates a json object and return the string.
I want to make a unit test on this function but the method return a string like this:
{"att1":"{\"scale\": 0, \"significand\": 10}","name":"john","lastname":"smith","job":"developper"}
If you copy-paste this line into commas, you don't get a string. And I can't use JSON.parseFull() because again, this is not a String. And I don't want my method to directly return a jsonObject.
I use this kind of object to create my json String
val objectMapper = new ObjectMapper()
val myJson= objectMapper.createObjectNode()
objectMapper.writeValueAsString(myJson)
And I use this code to make my unit test:
class MyJsonTest extends FlatSpec {
"My method" should "generate a valid json" in {
val myJsonString = getMyJson() //method to test
// this is not a valid String but my method return this:
val correctJson = "{"att1":"{\"scale\": 0, \"significand\": 10}","name":"john","lastname":"smith","job":"developper"}"
assert(correctJson === myJsonString )
}
Do you have any idea?
You should interpolate your string by using s""" YOUR_STRING HERE """, to take in consideration your "
class test extends FlatSpec {
def getSampleJson = """{"att1":"{"scale": 0, "significand": 10}","name":"john","lastname":"smith","job":"developper"}"""
"My method" should "generate a valid json" in {
val myJsonString = getSampleJson
// this is not a valid String but my method return this:
val correctJson =
s"""{"att1":"{\"scale\": 0, \"significand\": 10}","name":"john","lastname":"smith","job":"developper"}"""
println(myJsonString) // {"att1":"{"scale": 0, "significand": 10}","name":"john","lastname":"smith","job":"developper"}
println(correctJson) // {"att1":"{"scale": 0, "significand": 10}","name":"john","lastname":"smith","job":"developper"}
assert(correctJson === myJsonString)
}
}
So now, you can just call your method getJson :)

Create an instance of an object from a String/Symbol without the Class in Dart?

I know that it is possible to create an instance from a symbol like is shown in this link:
Create an instance of an object from a String in Dart?
But this doesn't work for me since what I want to do is create an instance without having the class.
This problem is caused because I have One class with an internal List:
class MyNestedClass {
String name;
}
class MyClass {
int i, j;
String greeting;
List<MyNestedClass> myNestedClassList;
}
And I want to convert a map to this class:
{
"greeting": "hello, there",
"i": 3,
"j": 5,
"myNestedClassList": [
{
"name": "someName1"
},{
"name": "someName2"
}
]
}
right now I am doing something like this:
static void jsonToObject(String jsonString, Object object) {
Map jsonMap = JSON.decode(jsonString); //Convert the String to a map
mapToObject(jsonMap, object); //Convert the map to a Object
}
static void mapToObject(Map jsonMap, Object object) {
InstanceMirror im = reflect(object); //get the InstanceMirror of the object
ClassMirror cm = im.type; //get the classMirror of the object
jsonMap.forEach((fieldNameStr, fieldValue) { // For each element in the jsonMap
var fieldName = new Symbol(fieldNameStr); // convert the fieldName in the Map to String
if (isPrimitive(fieldValue)) { // if fieldValue is primitive (num, string, or bool
im.setField(fieldName, fieldValue); //set the value of the field using InstanceMirror
} else if (fieldValue is List) { // else if the fieldValue is a list
ClassMirror listCm = (cm.declarations[fieldName] as VariableMirror).type; //get the class mirror of the list
var listReflectee = listCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
for(var element in fieldValue) { //for each element in the list
if(!isPrimitive(element)) { // if the element in the list is a map (i.e not num, string or bool)
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
//This is the line that doesn't work correctly
//It should be something like:
//
// ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
//
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
mapToObject(element, listObject); //convert the element to Object
}
listReflectee.add(element); //add the element to the list
};
} else { //else (the field value is a map
ClassMirror fieldCm = (cm.declarations[fieldName] as VariableMirror).type; // get the field ClassMirror from the parent declarations
var reflectee = fieldCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
mapToObject(fieldValue, reflectee); // convert the fieldValue, which is a map, to an object
im.setField(fieldName, reflectee); // set the value of the object previously converted to the corresponding field
}
});
}
As you can see the lines that are not actually working are:
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
since they are creating an instance on localClassMirror and not on MyNestedClass. I'm looking for a method similar to:
ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
you can see the full source code in the next URL:
DSON Source Code
If you have the full qualified name of the class you should be able to find the type using libraryMirror and then it should be similar to your linked question to create an instance.
I haven't done this myself yet and have not code at hand.
see also: how to invoke class form dart library string or file
An alternative approach would be to create a map at application initialization time where you register the supported types with their name or an id and look up the type in this map (this is like it's done in Go)