Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Extracting JSON data...

115 views
Skip to first unread message

James Moliere

unread,
May 3, 2024, 1:10:00 AM5/3/24
to TeaVM
Hello,
I am having trouble extracting JSON data to display.  I am using JDK 21 (with String Templates enabled).  I seemed to have run into a wall with Flavour (Templates.bind(...) doesn't seem to work) so I am trying Java's String Templates.

I made up a Rental application project as a Proof of Concept. The issue I am having is related to loading JSON data and rendering the display.

The following code does not seem to work (see Main below)...
=========================
String text xhr.getResponseText();
System.out.println(text);
JSObject json JSON.parse(text);
JSArray<RenterrenterJSArray json.cast();
renterListView.renderRenterJSArray(renterJSArray);
==========================

How can I convert the JSON text to a List or JSArray of Renter objects?

The error I am getting looks like this....
===============================
Uncaught TypeError: Cannot convert undefined to a BigInt
Long_ucompare main.js:453 jl_Long_compareUnsigned main.js:1665 umrtv_RenterListView_renderRow TAbstra…tringBuilder.java:151 otja_XMLHttpRequest$onComplete$static$lambda$_49_0_handleEvent$exported$0 RenterListView.java:37 (anonymous function) main.js:23 fn main.js:901 Async call from XMLHttpRequest.send umrt_Main_main main.js:855 (anonymous function) main.js:34 $rt_startThread main.js:742 (anonymous function) main.js:33 onload index.html:8
===================================


The Renter class look like...
==============================
public record Renterint ID,
 String renter,
 String address,
 String zip,
 Date active_date,
 Date inactive_date,
 String uid,
 BigDecimal cleaningDeposit)

{}
=============================

A sample JSON could be...
==============================
[
{
"renter": "Mickey Mouse",
"address": "1 Disney Way",
"last_paid": "Apr 4, 2024",
"amount": 1900,
"id": 1,
"month": 4,
"uid": "4ee56832",
"cleaning_deposit": 600
},
{
"renter": "Minney Mouse",
"address": "1 California Adeventure Way",
"last_paid": "Apr 7, 2024",
"amount": 700,
"id": 3,
"month": 4,
"uid": "ed3d8abc"
}
]
=====================================

A RenterListView class....
======================================
public class RenterListView {


List<Renter> renterList = new ArrayList<>();

final static String headerTemplate = STR. """
<script id="renter_template" type="text/x-handlebars-template">
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Address</th>
<th>Last month paid</th>
<th>Amount</th>
<th>Last Payment Date</th>
<th>UID</th>
<th>Cleaning Deposit</th>
</tr>
</table>
</script>
""";

public String renderRenterJSArray(JSArray<Renter> jsArray)
{
StringBuilder sb = new StringBuilder(headerTemplate);
for (int i=0;i<jsArray.getLength();i++)
{
Renter renter = jsArray.get(i);
String row = renderRow(renter, i);
sb.append(row);
}
return sb.toString();
}
public String renderRenterList(List<Renter> renterList)
{
this.renterList = renterList;
StringBuilder sb = new StringBuilder(headerTemplate);
int indexCounter = 0;
for (Renter renter : renterList) {

String row = renderRow(renter, indexCounter);
sb.append(row);
indexCounter++;
}
return sb.toString();
==============================================

And finally, Main...
===============================================
public class Main {

private static final HTMLDocument document = Window.current().getDocument();

static RenterListView renterListView = new RenterListView();

public static void main(String[] args) {

String html = renterListView.renderRenterList(Collections.emptyList());
XMLHttpRequest xhr = XMLHttpRequest.create();
xhr.onComplete(() -> Main.receiveResponse(xhr ));
int active =1;
int inactive = 0;
xhr.setResponseType("text/json");
xhr.open("GET", "/TeaVM-moliere/build/exploded/renterList.json");
xhr.send();

document.getBody().setInnerHTML(html);
// var window = Window.current();
// HTMLElement element = window.getDocument().getElementById("b");
// element.addEventListener("click", evt -> handleClick());
}

private static void receiveResponse(XMLHttpRequest xhr) {

if (xhr.getReadyState() != XMLHttpRequest.DONE) {
return;
}
String text = xhr.getResponseText();
System.out.println(text);
JSObject json = JSON.parse(text);
JSArray<Renter> renterJSArray = json.cast();
renterListView.renderRenterJSArray(renterJSArray);

}

public static void handleClick()
{
System.out.println("got called");
}
========================================


}
}

private static String renderRow(Renter renter, int indexCounter) {
String row = STR."""
<tr>
<td style="text-align: center">\{renter.ID()}</td>
<td>\{renter.renter()}</td>
<td>\{renter.address()}</td>
<td>{{ renter.asMonth month }}</td>
<td>{{renter.amount}} &nbsp;<button onclick="renderDialog( json[ \{indexCounter} ] )">Pay Rent</button> &nbsp;</td>
<td style="text-align: center">{{renter.last_paid}}</td>
<td>{{renter.uid}}</td>
<td style="text-align: right">{{renter.cleaning_deposit}}</td>
</tr>
""";
return row;
}
}

Alexey Andreev

unread,
May 3, 2024, 1:35:56 AM5/3/24
to TeaVM
The main mistake here is the assumption that JSON.parse produces JSArray<Renter>. It does not, since JSON.parse does not know anything about your Java class and how to construct it based on some JSON. The correct way would be to either declare something like:

interface RenterJSON extends JSObject {
    @JSProperty
    int getId();

    @JSProperty
    JSString getRenter();

     // etc
}

and get JSArray<RenterJSON> from JSON.parse, or to cast to JSArray<JSMapLIke<JSObject>> and then read it like this:

var id = ((JSNumber) jsonRenter.get("id")).intValue();
var renter = ((JSString) jsonRenter.get("renter")).stringValue();
// etc.

note that when JSON does not contain property at all, its value will be undefined not null. To test if value is undefined, use JSObjects.isUndefined() or instanceof JSUndefined.

пятница, 3 мая 2024 г. в 07:10:00 UTC+2, james....@gmail.com:

Ihromant

unread,
May 3, 2024, 6:13:50 PM5/3/24
to TeaVM
Hi. I had similar wish: to implement simple exchange in Java objects skipping JSON intermediate layer.
Earlier there was a library Flavour-JSON which served for this. But it was error-prone and eventually its support was dropped together with Flavour.
This forced me to write my own converters from and to JSON objects.
On server side parser-reader is Jackson, on client side it is my library.
Here is its source code:
It supports records and works more-less as Jackson ObjectMapper without annotations.
To make object transferrable, you should inherit it from ua.ihromant.teavm.io.Message (later I will show how I'm doing this).
Still, as mentioned above, you don't know what information are you deserializing. So, I'm transferring class information using the following trick:
public interface ChatMessage extends Message {
ChatType getMt();
}
public enum ChatType {
CONNECT_MESSAGE(IdentityMessage.class)// and other messages
private final Class<? extends Message> cls; // and getter
}
public record IdentityMessage(String user, String tab) implements ChatMessage {
@Override
public ChatType getMt() {
return ChatType.IDENTITY_MESSAGE;
}
}
Then when you receive serialized message, you will have something like (if you are using Jacksons .enable(SerializationFeature.WRITE_ENUMS_USING_INDEX) to serialize enums as integers) :
{"user":"abc","tab":"def","mt":0}
Using this info you just read a message in the following way:
JSMapLike<JSObject> jso = JSON.parse(data);
Class<?> cls ChatType.values()[jso.get("mt").<JSNumber>cast().intValue()].getCls();
return Converters.jsToJava(jso, cls);
Using Similarly you can serialize Java records etc in client side and send it to server side.
private JSObject convert(Object o) {
if (!(o instanceof ChatMessage m)) {
throw new IllegalArgumentException();
}
JSMapLike<JSObject> result = Converters.javaToJs(m).cast();
result.set("mt", JSNumber.valueOf(m.getMt().ordinal()));
return result;
}
public String write(Object o) {
return JSON.stringify(convert(o));
}

This implementation is simple and straightforward and I'm happily using it for 2+ years.
пʼятницю, 3 травня 2024 р. о 08:10:00 UTC+3 james....@gmail.com пише:

ScraM Team

unread,
May 20, 2024, 10:23:05 PM5/20/24
to TeaVM
To clarify, Flavour is alive and well at its new home: https://flavour.sourceforge.io/

I describe how to serialize and deserialize Java objects to/from JSON in Chapter 12.7 (Technique 4: Local State Caching) of the Flavour book: https://frequal.com/Flavour/book.html

It currently is based on TeaVM 0.6.1 and is therefore works best with Java 8. 
Reply all
Reply to author
Forward
0 new messages