How can I convert a map to an object? - java

I made a Map to store the values of all tags on a SOAPMessage body, so that the keys of the Map are the node names and the values are the text contents. I have an object where I already have the fields named after the node names, what I need to do is to set their values accordingly to their map counterparts.
Say that I have a node named "Summary" on the SOAPMessage, there will be a Map key named "Summary" and also an object field named "Summary". I need the object field "Summary" to be set as the value of the Map.get("Summary").
I know I could just fill my code with setters for each of the fields, but is there an way to set the entire object from the Map?
This is the method where I created the Map.
private static Map<String, String> mapIncidentInfo(SOAPMessage soapResponse) throws SOAPException {
Map<String, String> fields = new HashMap<String, String>();
NodeList nodes = soapResponse.getSOAPBody().getFirstChild().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
fields.put(node.getNodeName(), node.getTextContent());
}
return fields;
}
This could be used as an object class example:
public class IncidentPO {
private String Submitter;
private String Submit_Date;
private String Last_Modified_By;
private String Last_Modified_Date;
private String Status;
private String Short_Description;
public IncidentPO(String Submitter, String Submit_Date, String Last_Modified_By, String Last_Modified_Date, String Status, String Short_Description) {
this.Submitter = Submitter;
this.Submit_Date = Submit_Date;
this.Last_Modified_By = Last_Modified_By;
this.Last_Modified_Date = Last_Modified_Date;
this.Status = Status;
this.Short_Description = Short_Description;
//getters and setters here

There's no easy way (without libraries) to convert a Map to object. A direct option is to provide the Map in a constructor and have it populate itself.
public IncidentPO(Map<String, String> map) {
this.Submitter = map.get("Submitter");
this.Submit_Date = map.get("Submit_Date");
// etc
}

You can use object to json mapping then again json to object as below:
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class TestMap {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
Map<String, String> myMmap = new HashMap<>();
myMmap.put("name", "ABC");
myMmap.put("age", "20");
myMmap.put("sex", "male");
myMmap.put("city", "madhepura");
myMmap.put("spec", "java");
ObjectMapper mapper = new ObjectMapper();
ObjectNode objectNode1 = mapper.createObjectNode();
for(String key : myMmap.keySet()) {
objectNode1.put(key, myMmap.get(key));
}
// take the value of objectNode1.toString() and create a pojo from http://www.jsonschema2pojo.org/
Person person = mapper.readValue(objectNode1.toString().getBytes(), Person.class);
System.out.println(person);
}
}
// you can use http://www.jsonschema2pojo.org to get POJO from objectNode1.toString()
// {"city":"patna","sex":"male","name":"ABC","age":"20","spec":"java"}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"city",
"sex",
"name",
"age",
"spec"
})
class Person {
#JsonProperty("city")
private String city;
#JsonProperty("sex")
private String sex;
#JsonProperty("name")
private String name;
#JsonProperty("age")
private String age;
#JsonProperty("spec")
private String spec;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("city")
public String getCity() {
return city;
}
#JsonProperty("city")
public void setCity(String city) {
this.city = city;
}
#JsonProperty("sex")
public String getSex() {
return sex;
}
#JsonProperty("sex")
public void setSex(String sex) {
this.sex = sex;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("age")
public String getAge() {
return age;
}
#JsonProperty("age")
public void setAge(String age) {
this.age = age;
}
#JsonProperty("spec")
public String getSpec() {
return spec;
}
#JsonProperty("spec")
public void setSpec(String spec) {
this.spec = spec;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

Related

How to get index from an arrayList based on their weightage in java?

I have an ArrayList of transactions for a database in java. Each query has some weight associated with it. I want to execute that transaction that many number of times.
For eg putting 1 transaction in JSON format:-
{
"transaction": {
"name": "NewOrder",
"weight": 45,
"queries": [
{
"query": "select * from account where id > ? and balance > ?",
"bindParams": [
{
"utilityFunction": {
"name": "randomString",
"params": [
{
"minLen": 8,
"maxLen": 16
}
]
}
},
{
"utilityFunction": {
"name": "randomInteger",
"params": [
{
"minValue": 100000,
"maxLen": 100000
}
]
}
}
]
}
I have similar transactions with weights which add upto 100.
I now want to get the id of this transaction from the arraylist of transactions based on its weight.
For eg(transaction names and their weight):-
new order :-45(weight)
stockpurchase:- 30(weight)
newitems :- 15(weight)
deliveryitems :- 10 (weight)
I created an arrayList of integers which stores the sum till that index of transaction :-
[45,75,90,100]
Now I am thinking on invoking a random number[1-100] and get the index that lies closest to it to get the index from the arrayList of transactions.
Is this implementation correct or is there a more efficient way of doing this?
you need to convert your schema to POJO. like this
package com.example;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
public class BindParam {
private UtilityFunction utilityFunction;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public UtilityFunction getUtilityFunction() {
return utilityFunction;
}
public void setUtilityFunction(UtilityFunction utilityFunction) {
this.utilityFunction = utilityFunction;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package com.example;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
public class Param {
private Integer minLen;
private Integer maxLen;
private Integer minValue;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public Integer getMinLen() {
return minLen;
}
public void setMinLen(Integer minLen) {
this.minLen = minLen;
}
public Integer getMaxLen() {
return maxLen;
}
public void setMaxLen(Integer maxLen) {
this.maxLen = maxLen;
}
public Integer getMinValue() {
return minValue;
}
public void setMinValue(Integer minValue) {
this.minValue = minValue;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package com.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
public class Query {
private String query;
private List<BindParam> bindParams = null;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public List<BindParam> getBindParams() {
return bindParams;
}
public void setBindParams(List<BindParam> bindParams) {
this.bindParams = bindParams;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package com.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
#Generated("jsonschema2pojo")
public class Transaction {
private String name;
private Integer weight;
private List<Query> queries = null;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public List<Query> getQueries() {
return queries;
}
public void setQueries(List<Query> queries) {
this.queries = queries;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package com.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
#Generated("jsonschema2pojo")
public class UtilityFunction {
private String name;
private List<Param> params = null;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Param> getParams() {
return params;
}
public void setParams(List<Param> params) {
this.params = params;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
package com.example;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Generated;
public class WrapperClass {
private Transaction transaction;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
public class Main() {
public static void main(String[] args) {
//assume that you collect data from getValue()
List<WrapperClass> wrappers = getValue();
for(WrapperClass wrapper : wrappers) {
Transaction transaction = wrapper.getTransaction();
int weight = transaction.getWeight();
}
}
}
you can use JSONSchema2POJO to generate model from json.

How to map same #JsonAlias to different object fields using Jackson ObjectMapper

In my DTO class, there could be fields which might have same #JsonAlias values, but this seems not working with Jackson ObjectMapper.
It seems that ObjectMapper only works for the first occurrence of #JsonAlias and it don't work for the rest #JsonAlias which has same value. I have tried to create an example below for the reference and in that example, Person class has the two fields with name #JsonAlias value.
Code snippet:
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String, String> values = new HashMap<>();
values.put("name", "TEST NAME");
Person person = new ObjectMapper().convertValue(values, Person.class);
System.out.println("name1: " + person.getName1());
System.out.println("name2: " + person.getName2());
}
static class Person {
#JsonAlias("name")
String name1;
#JsonAlias("name")
String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
}
}
Output:
name1: TEST NAME
name2: null
In the above output. I was expecting the "TEST NAME" for the name2 variable in Person class.
Is there any configuration in Jackson ObjectMapper which will help me to achieve this?
Jackson version - jackson-databind-2.11.4.jar
Solution 1, #JsonGetter #JsonSetter point out to the custom getter\setter
We can use #JsonGetter("name") and #JsonSetter("name") annotations to map one JSON property value to the setter which has handling two properties
public class Person {
#JsonIgnore
private String name1;
#JsonIgnore
private String name2;
#JsonSetter("name")
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
#JsonGetter("name")
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
}
Solution 2, #JsonAlias with the specific setter
We can ignore one field, the second mark with an alias, and add a custom setName setter that maps one JSON value to several fields.
public class Person {
#JsonAlias("name")
private String name1;
#JsonIgnore
private String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
Or just add a custom setName setter to your original code
public class Person {
#JsonAlias("name")
String name1;
#JsonAlias("name")
String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
Solution 3, #JsonProperty with the specific setter
The same like Solution 2, but instead use #JsonProperty("name")
public class Person {
#JsonProperty("name")
private String name1;
#JsonIgnore
private String name2;
public String getName1() {
return name1;
}
#JsonIgnore
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
UPDATE:
In case you do not have the ability to modify DTO object, custom deserializer resolve the problem.
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class PersonDeserializer extends StdDeserializer<Person> {
public PersonDeserializer() {
this(null);
}
public PersonDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Person person = new Person();
JsonNode node = jp.getCodec().readTree(jp);
String name = node.get("name").asText();
person.setName1(name);
person.setName2(name);
return person;
}
}
Apply deserializer to the DTO object:
#JsonDeserialize(using = PersonDeserializer.class)
public class Person {
private String name1;
private String name2;
//getters and setters
}
OR register custom deserializer on ObjectMapper
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Person.class, new PersonDeserializer());
mapper.registerModule(module);

Trouble with understanding exception: "Cannot deserialize instance of `java.lang.String` out of START_OBJECT token" using ObjectMapper in Jackson

I cannot properly convert a JSON Object into POJO. I kind of understand where is the problem, but can't figure out, how to deal with it.
Here's all specific data to understand the issue:
JSONObject which I try to deserialize (understanding the values names isn't key to understand the problem):
[{"name":"Rafał","description":"Przykładowy opis profilu","location":"Lublin","interests":[{"0":"Gry komputerowe","1":"Muzyka","2":"Siłownia"}],"age":24,"rowid":2,"username":"lenivius"}]
My POJO class:
public class Users {
private int rowid = 0, age;
private String name, username, e_mail, password, description, location;
private List<String> interests;
public Users() {
}
public Users(int rowid, int age, String name, String username, String e_mail, String password, String description, String location, List<String> interests) {
this.setRowid(rowid);
this.setAge(age);
this.setName(name);
this.setUsername(username);
this.setE_mail(e_mail);
this.setPassword(password);
this.setDescription(description);
this.setLocation(location);
this.setInterests(interests);
}
public int getRowid() {
return rowid;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getUsername() {
return username;
}
public String getE_mail() {
return e_mail;
}
public String getPassword() {
return password;
}
public String getDescription() {
return description;
}
public String getLocation() {
return location;
}
public void setRowid(int rowid) {
this.rowid = rowid;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setUsername(String username) {
this.username = username;
}
public void setE_mail(String e_mail) {
this.e_mail = e_mail;
}
public void setPassword(String password) {
this.password = password;
}
public void setDescription(String description) {
this.description = description;
}
public void setLocation(String location) {
this.location = location;
}
public List<String> getInterests() {
return interests;
}
public void setInterests(List<String> interests) {
this.interests = interests;
}
}
And lastly code line which causes the exception to happen:
resultUsers = objectMapper.readValue(responseString, Users[].class);
I can also post full exception message:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (String)"[{"name":"Rafał","description":"Przykładowy opis profilu","location":"Lublin","interests":[{"0":"Gry komputerowe","1":"Muzyka","2":"Siłownia"}],"age":24,"rowid":2,"username":"lenivius"}]"; line: 1, column: 92] (through reference chain: java.lang.Object[][0]->com.example.loveterests.Users["interests"]->java.util.ArrayList[0])
If I understand the problem correctly, then Jackson needs a list of String objects to properly convert JSON into POJO, but inside "interests" key there is a JSON Array, and that's probably where all the is the problem.
Problem is with interests property. In POJO it is represented by List<String> and in JSON Payload by JSON Array[JSON Object] - array with objects, not strings. You can use Map<String, Object> type to handle this:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#AllArgsConstructor
class Users {
private int rowid = 0, age;
private String name, username, e_mail, password, description, location;
private List<Map<String, Object>> interests;
}

Deserializing Enum, which contains Map

my enum:
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Currency {
USD("USD", "United States Dollar"),
EUR("EUR", "Euro"),
BGP("BGP", "British Pound"),
AUD("AUD", "Australian Dollar"),
CAD("CAD", "Canadian Dollar");
private final String shortName;
private final String fullName;
private Map<Enum, Double> rates;
Currency(String shortName, String fullName) {
this.shortName = shortName;
this.fullName = fullName;
this.rates = new HashMap<>();
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
public Map<Enum, Double> getRates() {
return rates;
}
}
Postman response I get from another rest api:
{
"shortName": "EUR",
"fullName": "Euro",
"rates": {
"AUD": 1.62,
"CAD": 1.47,
"USD": 1.11,
"BGP": 0.86,
"EUR": 1.0
}
}
Title pretty much sums up what I need. Any ideas how to serialize the postman response in my code, so i have enum as a result, which contains all properties, including "rates" map?
Thanks in advance.
You need to create a static factory method annotated with com.fasterxml.jackson.annotation.JsonCreator annotation. In case when whole JSON Object represents enum, Jackson automatically converts it to Map, so this method should have signature like below:
#JsonCreator
public static Currency from(Map<String, Object> value)
Below showcase provides complete implementation:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
Currency currency = mapper.readValue(jsonFile, Currency.class);
System.out.println(currency + " => " + currency.getRates());
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum Currency {
USD("USD", "United States Dollar"),
EUR("EUR", "Euro"),
BGP("BGP", "British Pound"),
AUD("AUD", "Australian Dollar"),
CAD("CAD", "Canadian Dollar");
private final String shortName;
private final String fullName;
private Map<Enum, Double> rates;
Currency(String shortName, String fullName) {
this.shortName = shortName;
this.fullName = fullName;
this.rates = Collections.emptyMap();
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
public Map<Enum, Double> getRates() {
return rates;
}
public static Currency fromShortName(String value) {
for (Currency currency : values()) {
if (currency.getShortName().equals(value)) {
return currency;
}
}
throw new IllegalArgumentException(value + " is not found!");
}
#JsonCreator
public static Currency from(Map<String, Object> value) {
String shortName = value.getOrDefault("shortName", "").toString();
Currency currency = fromShortName(shortName);
Map<String, Double> rates = (Map<String, Double>) value.getOrDefault("rates", Collections.emptyMap());
Map<Enum, Double> newRates = new HashMap<>(9);
rates.forEach((k, v) -> {
newRates.put(Currency.fromShortName(k), v);
});
currency.rates = Collections.unmodifiableMap(newRates);
return currency;
}
}
Above code prints:
EUR => {EUR=1.0, AUD=1.62, CAD=1.47, USD=1.11, BGP=0.86}
Warning
enum should be an Immutable object and keeping rates Map inside is not a good idea. You should definitely try to remove it from enum. In multi thread environment you need to guarantee that this Map will not be change during the read. So, in implementation I do not change already used Map but create new every time.

TableView<T> does not display the content of ObservableList<T> objects

I have two almost similar custom classes for storing simple String data - they are called "Patient" and "Trace".They differ from eachother only by the number of defined fields. Constructors of both are shown below (with getVariablesNames() static method):
public class Patient {
String patientID;
String firstName;
String lastName;
String gender;
String dateOfBirth;
String age;
String email;
String phoneNumber;
String city;
public Patient(String patientID, String firstName, String lastName, String gender, String dateOfBirth, String age, String email, String phoneNumber, String city) {
this.patientID = patientID;
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.dateOfBirth = dateOfBirth;
this.age = age;
this.email = email;
this.phoneNumber = phoneNumber;
this.city = city;
}
public static String[] getVariablesNames() {
Field[] fields = Patient.class.getDeclaredFields();
String[] variablesNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
variablesNames[i] = fields[i].getName();
}
return variablesNames;
}
public String getPatientID() {
return patientID;
}
public void setPatientID(String value) {
patientID = value;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String value) {
firstName = value;
}
public String getLastName() {
return lastName;
}
public void setLastName(String value) {
lastName = value;
}
public String getGender() {
return gender;
}
public void setGender(String value) {
gender = value;
}
public String getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(String value) {
dateOfBirth = value;
}
public String getAge() {
return age;
}
public void setAge(String value) {
age = value;
}
public String getEmail() {
return email;
}
public void setEmail(String value) {
email = value;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String value) {
phoneNumber = value;
}
public String getCity() {
return city;
}
public void setCity(String value) {
city = value;
}
}
And constructor for "Trace" class:
public class Trace {
String action;
String status;
String message;
String time;
public Trace(String action, String status, String message, String time) {
this.action = action;
this.status = status;
this.message = message;
this.time = time;
}
public static String[] getVariablesNames() {
Field[] fields = Trace.class.getDeclaredFields();
String[] variablesNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
variablesNames[i] = fields[i].getName();
}
return variablesNames;
}
public void setActionText(String value) {
action = value;
}
public String getActionText() {
return action;
}
public void setStatusText(String value) {
status = value;
}
public String getStatusText() {
return status;
}
public void setMessageText(String value) {
message = value;
}
public String getMessageText() {
return message;
}
public void setTimeText(String value) {
time = value;
}
public String getTimeText() {
return time;
}
}
I use objects of those classes to populate custom TableView<T> instances, where <T> can be "Patient" or "Trace". The problem I encounter is that "Trace" object values are not displayed in the table, where there are no problems with objects of the "Patient" class. The custom TableView<T> class is shown below:
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
public class TableViewCustom<T> extends TableView<T> {
public TableViewCustom(String[] columnNames, String[] variablesNames, ObservableList<T> data) {
super();
#SuppressWarnings("unchecked")
TableColumn<T, String>[] tableColumns = new TableColumn[variablesNames.length];
for (int i = 0; i < tableColumns.length; i++) {
tableColumns[i] = new TableColumn<T, String>(columnNames[i]);
tableColumns[i].setCellValueFactory(new PropertyValueFactory<T, String>(variablesNames[i]));
}
this.setItems(data);
this.getColumns().addAll(tableColumns);
this.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
}
}
And the example implementation of this custom TableView with use of both "Patient" and "Trace" objects:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Demo extends Application {
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
VBox tableViewsContainer = new VBox(5);
/* layout -> center -> top */
String[] patientColumnNames = new String[] {"Patient ID", "First Name", "Last Name", "Gender", "Date Of Birth", "Age", "Email", "Phone Number", "City"};
String[] patientVariablesNames = Patient.getVariablesNames();
ObservableList<Patient> patientData = FXCollections.observableArrayList(new Patient("Patient ID", "First Name", "Last Name", "Gender", "Date Of Birth", "Age", "Email", "Phone Number", "City"));
TableViewCustom<Patient> patientTableView = new TableViewCustom<Patient>(patientColumnNames, patientVariablesNames, patientData);
/* layout -> center -> bottom */
String[] traceColumnNames = new String[] {"Action", "Status", "Message", "Time"};
String[] traceVariablesNames = Trace.getVariablesNames();
ObservableList<Trace> traceData = FXCollections.observableArrayList(new Trace("Action", "Status", "Message", "Time"));
TableViewCustom<Trace> traceTableView = new TableViewCustom<Trace>(traceColumnNames, traceVariablesNames, traceData);
/* add items to the layout */
tableViewsContainer.getChildren().addAll(patientTableView, traceTableView);
layout.setCenter(tableViewsContainer);
return layout;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(700);
stage.setHeight(400);
stage.show();
}
public static void main(String args[]) {
launch(args);
}
}
The result of Demo.java app is shown below:
PS I'm not getting any errors.
The problem is that the values you are passing to the PropertyValueFactory do not match the names of the properties, which are defined by the get...() and set...(...) methods.
Since you are using reflection to look up the names of the fields (not the properties) you have defined, for the Trace class the values passed to PropertyValueFactory are "action", "status", "message", and "time". So the table view will attempt to populate the values for the columns by calling getAction(), getStatus(), getMessage(), and getTime() on the Trace objects in each row. Since there are no such methods, you don't get any values displayed.
To fix this, you can do one of the following:
Hard code the values defined in your getVariablesNames() method to return the names of the properties (i.e. return new String[] {"actionText", "statusText", "messageText", "timeText"};). Since you repeat the method in each class rather than reusing it, you don't make things more verbose, and this would potentially perform better (though you are unlikely to observe any differences in practice).
Use reflection, but look up the names of the declared methods, find all those beginning "get" or "is", strip off that prefix, and lower-case the first character of what remains.
Make the field names match the property names (i.e. declare the fields as actionText, etc.). This of course imposes convention requirements on your class definition.

Categories

Resources