For a school assignment, I need to parse a CSV into a Bean and present it in a JavaFX GUI later. I decided to use the Library opencsv, which worked fine.
But now, I would like to parse the attributes directly into SimpleObjectProperties. How do I do that? Unfortunately, I couldn't find any further information.
Code looks like this:
public class Phone {
#CsvBindByName(column = "ENTITY_ID")
private SimpleIntegerProperty entityId;
#CsvBindByName(column = "OPERATING_COMPANY")
private SimpleStringProperty operatingCompany;
When I run the code, I get a CsvDataTypeMismatchException (Conversion of 1006 to javafx.beans.property.SimpleIntegerProperty failed).
Any help much appreciated, thank you!!
Looking at the documentation it looks like you can create CustomConverts for each type of property you have; the example they have on the documentation page, this is the start to an IntegerPropertyConverter.
public class IntegerPropertyConverter extends AbstractCsvConverter {
#Override
public Object convert(String value) {
return new SimpleIntegerProperty(Integer.parseInt(value));
}
#Override
public String convertToWrite(Object value) {
IntegerProprety prop = (IntegerProperty) value;
return String.format("%d", prop.get());
}
}
Then you'd use:
#CsvCustomBindByName(column = "ENTITY_ID", converter = IntegerPropertyConverter.class)
private SimpleIntegerProperty entityId;
If you need to create your properties using the longer format, you will need to override other methods in the AbstractBeanField, such as public final void setFieldValue(T bean, String value, String header) where you can actually use the bean to create the
There is no easy way around this.
You keep your Phone a POJO and map the entire object as a property
private SimpleObjectProperty<Phone> phone = new SimpleObjectProperty<Phone>();
Or you could add properties to the Phone
public class Phone {
#CsvBindByName(column = "ENTITY_ID")
private Integer entityId;
private final SimpleIntegerProperty entityIdProperty;
public Phone() {
entityIdProperty = new SimpleIntegerProperty();
entityIdProperty.addListener((o, oldValue,newValue)->{
entityId = newValue.intValue();
});
}
public Integer getEntityId() {
return entityId;
}
public void setEntityId(Integer entityId) {
this.entityId = entityId;
entityIdProperty.set(entityId);
}
public SimpleIntegerProperty getEntityIdProperty() {
return entityIdProperty;
}
// ...
}
If you do not need this object to have a bidirectional binding you could skip the listener.
There are other possibilities too, like having Methods and constructors to convert from a Phone to a PhoneFX (with properties instead of simple types) and viceversa.
Related
I have a very simple Entity (Person.java) that I am wanting to persist via JPA/Hibernate.
The Entity contains two fields: ID and Identification String.
The ID is a simple Integer, and is no problem. The Identification String is currently a String, but for various reasons, I want to instead use a wrapper class for String (IDString), where there are various validation methods among other things.
I am wondering how I can get JPA/Hibernate to use the wrapped string (inside the custom class IDString) when persisting the Person table in the database. I know this can probably be solved by letting the IDString be #Embeddable and then embed IDString in the Person entity with #Embedded, but I am looking for another method, mostly because IDString is in an entirely different package, and I am reluctant to have to go there and change stuff.
Googling, I found https://www.baeldung.com/hibernate-custom-types, but it seems to be mostly about more complicated cases, where you want to convert one class into another type, and I do feel that there is probably a smarter way that I am simply overlooking.
Here is the entity (in theory)
#Entity(name="Person")
#Table(name="DB_TABLE_PERSON")
public class Person implements Serializable {
#Id
Integer id;
// WHAT SHOULD I PUT HERE? I WANT TO SIMPLY USE THE STRING INSIDE IDSTRING AS THE FIELD TO PERSIST
IDString idString;
// getter and setter for ID.
public void getIdString() {
return idString.getValue();
}
public void setIdString(String in) {
idString.setValue(in);
}
}
And here is the class IDString (in theory):
public class IDString {
// I really want to be a POJO
private final String the_string;
public IdString(String input) {
if (isValid(input)) {
the_string = input;
} else {
throw new SomeCoolException("Invalid format of the ID String");
}
public boolean isValid(String input) {
// bunch of code to validate the input string
}
public String getValue() {
return the_string;
}
public void setValue(String input) {
if (isValid(input)) the_string = s;
else throw new SomeCoolException("Invalid format of the ID String");
}
I know that I could place the validation if the IDString inside the Entity, but the IDString will be used elsewhere (it's a general custom class), so I don't want to do that. Is there a simple way?
#Converter(autoApply=true) // autoApply is reasonable, if not use #Converter on field
public class IDStringConverter implements AttributeConverter<IDString,String> {
#Override
public String convertToDatabaseColumn(IDString attribute) {
return attribute != null ? attribute.getValue() : null;
}
#Override
public IDString convertToEntityAttribute(String dbData) {
return dbData != null ? new IDString(dbData) : null;
}
}
With this you should not need any other modifications in your code. One limitation of the AttributeConverter is that it maps from exactly 1 Java field to exactly 1 DB column. If you wanted to map to more columns (not the case here), you would need embeddables.
You could also put a #Column annotation on the getter:
#Entity
public class Person {
private final IdString idString = new IdString();
#Column(name = "ID_STRiNG")
public IdString getIdString() {
return idString.getValue();
}
public void setIdString(String input) {
idString.setValue(input);
}
Another solution could be to convert to/from IdString using #PostLoad and #PrePersit event handlers:
#Entity
public class Person {
#Column(name = "ID_STRiNG")
private String the_string; // no getters & setters
#Transient
private final IdString idString = new IdString();
#PostLoad
public void postLoad() {
idString.setValue(the_string);
}
#PrePersist
public void prePersist() {
the_string = idString.getValue();
}
// getters & setters for idString
I use ModelMapper in my project to map between DTO classes and models.
For example:
public class UserDto {
private String name;
private String phone;
private String email;
}
public class User {
#Id
private String id;
private String metaDatal;
private String name;
private String phone;
private String email;
}
Here How I map it:
#Autowired
private ModelMapper modelMapper;
modelMapper.map(userDto, user);
As you can see I have metaDatal field in the user model, I want to set this field with a specific value.
Specific field(metaDatal) of the mapped class I want to set this value "abc123".
Is there any way to tell map method when it called that, specific filed(for example metaData) should have specific value(for example abc123)?
I believe the most flexible way to do this is to implement a simple Converter. Check this:
Converter<UserDto, User> metaData = new Converter<UserDto, User>() {
// This is needed to convert as usual but not having not registered
// this converter to avoid recursion
private final ModelMapper mm = new ModelMapper();
#Override
public User convert(MappingContext<UserDto, User> context) {
User u = context.getDestination();
mm.map(context.getSource(), u);
u.setMetaDatal("abc123");
return context.getDestination();
}
};
Now it is just to create a TypeMap and set this converter to handle conversion, like:
modelMapper.createTypeMap(UserDto.class, User.class).setConverter(metaData);
before modelMapper.map().
You could also add a getter for metadata in your UserDto, like:
public String getMetaDatal() {
return "abc123";
}
If it is something that can be derived from UserDto directly and skip the converter part.
I have a class like:
class Car {
private Engine myEngine;
#JsonProperty("color")
private String myColor;
#JsonProperty("maxspeed")
private int myMaxspeed;
#JsonGetter("color")
public String getColor()
{
return myColor;
}
#JsonGetter("maxspeed")
public String getMaxspeed()
{
return myMaxspeed;
}
public Engine getEngine()
{
return myEngine;
}
}
and Engine class like
class Engine {
#JsonProperty("fueltype")
private String myFueltype;
#JsonProperty("enginetype")
private String myEnginetype;
#JsonGetter("fueltype")
public String getFueltype()
{
return myFueltype;
}
#JsonGetter("enginetype")
public String getEnginetype()
{
return myEnginetype;
}
}
I want to convert the Car object to JSON using Jackson with structure like
'car': {
'color': 'red',
'maxspeed': '200',
'fueltype': 'diesel',
'enginetype': 'four-stroke'
}
I have tried answer provided in this but it doesn't work for me as field names are different then getter
I know I can use #JsonUnwrapped on engine if field name was engine. But how to do in this situation.
provide #JsonUnwrapped and #JsonProperty together:
#JsonUnwrapped
#JsonProperty("engine")
private Engine myEngine;
You shall use the #JsonUnwrapped as follows in the Car class for the desired JSON object:
class Car {
#JsonUnwrapped
private Engine myEngine;
#JsonProperty("color")
private String myColor;
#JsonProperty("maxspeed")
private int myMaxspeed;
...
I think the best solution here would be to use #JsonValue annotation over the myEngineType attribute in your Engine class, it will only serialize this attribute instead of the whole Engine object.
So your code would be like this:
class Engine {
#JsonProperty("fueltype")
private String myFueltype;
#JsonValue
#JsonProperty("enginetype")
private String myEnginetype;
}
You can take a look at this answer for more details.
I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}
I try to use only immutables objects in my application. I've got a REST service that will take arbitrary JSon objects as input.
I've a Java class that map theses objects, and I want to make them immutable + able to deal with extra parameters (just like using #JsonAnySetter).
Here is my java class:
public class Operation {
private final String _id;
private final String state;
private final Map<String, Object> extra;
public Operation(String _id, String state, Map<String,Object> extra) {
this._id = _id;
this.state = state;
this.extra = extra;
}
// getters....
}
Using #JsonAnySetter I would have:
public class Operation {
private final String _id;
private final String state;
private Map<String, Object> extra = new HashMap<>();
public Operation(String _id, String state) {
this._id = _id;
this.state = state;
}
#JsonAnySetter
public void addExtra(String key, Object value) {
this.extra.put(key,value);
}
// getters....
}
But this is not immutable anymore !
This will not work because Jackson do not find any "extra" json attribute to read. I would like that everything that cannot be mapped be added to my map.
Any idea of how to do this ? (or is it just possible :)
Note: I use javac with -parameters option and the ParameterNameModule from jackson so that I don't need #JsonCreator option.
Ok so I respond to myself :)
It seems that it is not possible to do that using only Jackson.
Because I want immutability, I've turned myself to the 'immutables' framework: http://immutables.github.io/
With a little configuration, it will deal with extra parameters as stated in the following report: https://github.com/immutables/immutables/issues/185.
In my situation, I've got the following code:
#Value.Immutable
#JsonSerialize(as = ImmutableOperation.class)
#JsonDeserialize(as = ImmutableOperation.class)
public abstract class Operation {
#JsonAnyGetter
#Value.Parameter
public abstract Map<String, String> extra();
}
Refer to the documentation of immutables for the details.
If you want to deserialize immutable entity with extra arguments you can utilize builder pattern:
#JsonPOJOBuilder
public class OperationBuilder {
private String _id;
private String _state;
private Map<String, Object> extra = new HashMap<>();
#JsonAnySetter
public OperationBuilder addExtra(String key, Object value) {
this.extra.put(key,value);
return this;
}
// setters....
public Operation build() {
return new Operation(...arguments...)
}
And your original class should have this annotation on a class level:
#JsonDeserializer(builder = OperationBuilder.class)
This way all your known and unknown (extra) fields will be populated inside the builder and then Jackson will call build() method at the end of the deserialization.