2

I am developing a generic editor for JSON Array using JavaFX.
The display in the table in such a way that the columns will be the keys, and the value in the rows will be more descriptive. There can be a different number of keys in one JSONObject.

JSON of the form:
"[{\"key1\": 1, \"key2\": 2}, {\"key1\": 3, \"key2\": 4}]"

It needs to look like this:

key1 key2
1 2
3 4

Have any suggestions?

Argun
  • 51
  • 6
  • 3
    Create a class that models the `JSON` data. Use a program like `GSON` to parse the data as a `List` of objects. Follow any beginner `TableView` tutorial from there. – SedJ601 Dec 10 '21 at 09:17
  • @Sedrick how to find out the number of fields? – Argun Dec 10 '21 at 09:27
  • 1
    I know you're asking about `TableView` but [here is a way to show JSON in a `TreeView`](https://stackoverflow.com/a/61016095/6028807). – Miss Chanandler Bong Dec 10 '21 at 10:31
  • 2
    _how to find out the number of fields_ that's unrelated to javafx - first learn how to use JSON, then build the UI around :) – kleopatra Dec 10 '21 at 12:02

1 Answers1

5

This can be broken down into two parts.

  1. Use GSON to parse a JSON Array to an Array of POJOs.
  2. Display a List of Objets in a TableView.

Key Code

//Add data to the TableView!
String jsonString = "[{\"keyOne\":\"1\", \"keyTwo\":\"2\"}, {\"keyOne\":\"3\", \"keyTwo\":\"4\"}]";
Gson gson = new Gson();
Data[] dataList = gson.fromJson(jsonString, Data[].class);
ObservableList<Data> observableList = FXCollections.observableArrayList(dataList);
tableView.setItems(observableList);

Main

import com.google.gson.Gson;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.scene.layout.StackPane;


public class App extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage){         
        TableView<Data> tableView = new TableView();

        TableColumn<Data, String> column1 = new TableColumn<>("Key One");
        column1.setCellValueFactory((cdf) -> new SimpleStringProperty(cdf.getValue().getKeyOne()));


        TableColumn<Data, String> column2 = new TableColumn<>("Key Two");
        column2.setCellValueFactory((cdf) -> new SimpleStringProperty(cdf.getValue().getKeyTwo()));


        tableView.getColumns().add(column1);
        tableView.getColumns().add(column2);

        //Add data to the TableView!
        String jsonString = "[{\"keyOne\":\"1\", \"keyTwo\":\"2\"}, {\"keyOne\":\"3\", \"keyTwo\":\"4\"}]";
        Gson gson = new Gson();
        Data[] dataList = gson.fromJson(jsonString, Data[].class);
        ObservableList<Data> observableList = FXCollections.observableArrayList(dataList);
        tableView.setItems(observableList);
    
        Scene scene = new Scene(new StackPane(tableView));

        stage.setTitle("JavaFX 13");
        stage.setScene(scene);
        stage.show();
    }
} 

Data Class

/**
 *
 * @author sedj601
 */
public class Data {
    private String keyOne;
    private String keyTwo;
    

    public Data(String keyOne, String keyTwo) {
        this.keyOne = keyOne;
        this.keyTwo = keyTwo;
    }

    public String getKeyOne() {
        return keyOne;
    }

    public void setKeyOne(String keyOne) {
        this.keyOne = keyOne;
    }

    public String getKeyTwo() {
        return keyTwo;
    }

    public void setKeyTwo(String keyTwo) {
        this.keyTwo = keyTwo;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Data{keyOne=").append(keyOne);
        sb.append(", keyTwo=").append(keyTwo);
        sb.append('}');
        return sb.toString();
    }    
}

module-info.java

module com.mycompany.javafx_test_2 {
    requires javafx.controls;
    exports com.mycompany.javafx_test_2;
    
    opens com.mycompany.javafx_test_2 to com.google.gson;
    requires com.google.gson;
    
}

Using GSON version 2.8.9.

Output

enter image description here

SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • 2
    looks like an excellent answer for all those _JSON in TableView_ questions :) – kleopatra Dec 10 '21 at 12:03
  • Great answer. Can I suggest a couple of improvements to the programming style (since this might be used as a general example)? `tableView` should not be a raw type, i.e. it should be `TableView tableView = new TableView<>();`, and the fields in `Data` should be `private`, not `public`. – James_D Dec 10 '21 at 12:56
  • @James_D, Good catch on the `TableView's` type. I think I copied that code from the official Java site. I could be wrong. Also, I originally had the fields private. `GSON`/the application would not compile with them being private. I could not figure out why. If they are private, I think I would have to do more in the module file. `Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String com.mycompany.javafx_test_2.Data.keyOne accessible: module com.mycompany.javafx_test_2 does not "opens com.mycompany.javafx_test_2" to module com.google.gson`. – SedJ601 Dec 10 '21 at 15:28
  • 1
    Ugh. GSON uses reflection to set the fields (which is dumb; it should use the accessor methods). It's still better to make them private and open your package to the gson module, though. – James_D Dec 10 '21 at 15:32
  • Okay, I changed them to private. Now, I added `opens com.mycompany.javafx_test_2 to com.google.gson;` to the `module-info` file. – SedJ601 Dec 10 '21 at 15:33
  • 1
    I suggest that you put the module-info.java file in the answer. – jewelsea Dec 10 '21 at 16:09
  • @jewelsea. Good idea! – SedJ601 Dec 10 '21 at 16:52
  • 2
    fyi: added a reference to this QA to the faq – kleopatra Dec 11 '21 at 17:04
  • 1
    Best [not to use `PropertyValueFactory`](https://stackoverflow.com/questions/70302119/show-json-in-tableview). Also, perhaps if the data is read-only, a `record` could be used to represent it, though I don't know if GSON handles records well, I know Jackson can. – jewelsea Jan 18 '23 at 14:07
  • 1
    @jewelsea, I updated the code and replaced `PropertyValueFactory`. I am not a fan of `Jackson` for various reasons. Maybe I should give it another try. According to [this](https://github.com/google/gson/releases), `GSON` does support `Record`. I did not change that in the code, though. – SedJ601 Jan 18 '23 at 17:37