class Person {
public String name;
public String nickname;
}
When converting a POJO from Object to String, I want to exclude any values which are null, so that if I have Person = {name = "Jack", nickname = null}, the toString() will just return {name = "Jack"}
Is this use case possible?
Edit: Yes, I am using Lombok. Maybe the solution is to implement my own toString()
References:
https://groups.google.com/forum/#!topic/project-lombok/YC-eo2_XmsI
As for now, this feature of omiting nulls is NYI in lombok, so you will have to implement your own toString method.
You can either use StringJoiner 1 and skip nulls explicitly, or you can use a helper ToStringHelper 2 from Guava
class Person {
public String name;
public String nickname;
#Override
public String toString() {
final var joiner = new StringJoiner(", ", Person.class.getSimpleName() + "[", "]");
if (name != null) {
joiner.add("name='" + name + "'");
}
if (nickname != null) {
joiner.add("nickname='" + nickname + "'");
}
return joiner.toString();
}
#Override
public String toString() {
return MoreObjects.toStringHelper(this)
.omitNullValues()
.add("name", name)
.add("nickname", nickname)
.toString();
}
}
You can use the commons-lang3 to achieve your aim.
The only one thing what you need to do is to override the toString() method in the Person class:
public class Person {
private String name;
private String nickName;
// getters and setters
#Override
public String toString() {
final Object myself = this;
ReflectionToStringBuilder builder = new ReflectionToStringBuilder(this, ToStringStyle.JSON_STYLE) {
#Override
protected boolean accept(Field field) {
try {
return super.accept(field) && field.get(myself) != null;
}
catch (IllegalAccessException e) {
return super.accept(field);
}
}
};
return builder.toString();
}
}
The result if the name field value is not null but nickName is null:
{"name":"John"}
You need to add the following dependency to your pom.xml:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
The benefit of the usage of the ReflectionToStringBuilder class is that you do not need to touch your toString() method if you add a new class member variable because this class uses reflection.
Related
Is there a way ModelMapper can reverse map a value already set using PropertyMap again to another field in destination? In other words, I have a computed value and the same has to be set on two fields using PropertyMap, computation changes the value everytime and cannot be invoked twice using(converter).
Introduction
Let's consider the following versions as the current versions:
ModelMapper: 3.1.0.
It seems that by the «ModelMapper» library design:
There is no way to «reuse» the converted source value to set multiple destination property values.
Possible solution
A post-converter may be used to copy a value from one destination property to another.
Draft example program
pom.xml file
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.0</version>
</dependency>
Customer class
class Customer {
private String fullName;
public String getFullName() {
return fullName;
}
public void setFullName(final String fullName) {
this.fullName = fullName;
}
}
CustomerDTO class
import java.util.StringJoiner;
class CustomerDTO {
private String fullName;
private String copyOfFullName;
public String getFullName() {
return fullName;
}
public void setFullName(final String fullName) {
this.fullName = fullName;
}
public String getCopyOfFullName() {
return copyOfFullName;
}
public void setCopyOfFullName(final String copyOfFullName) {
this.copyOfFullName = copyOfFullName;
}
#Override
public String toString() {
return new StringJoiner(", ", CustomerDTO.class.getSimpleName() + "[", "]")
.add("fullName='" + fullName + "'")
.add("copyOfFullName='" + copyOfFullName + "'")
.toString();
}
}
Program class
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
public final class Program {
public static void main(final String[] args) {
final Customer customer = new Customer();
customer.setFullName("Full name");
final ModelMapper modelMapper = new ModelMapper();
final Converter<Customer, CustomerDTO> customerConverter = context -> {
final CustomerDTO destination = context.getDestination();
// NOTE: Copying the value of the full name property to another property.
destination.setCopyOfFullName(destination.getFullName());
return destination;
};
modelMapper.typeMap(
Customer.class, CustomerDTO.class
).addMapping(
Customer::getFullName, CustomerDTO::setFullName
).setPostConverter(customerConverter);
modelMapper.validate();
final CustomerDTO customerDTO = modelMapper.map(customer, CustomerDTO.class);
System.out.println(customerDTO);
}
}
Output
CustomerDTO[fullName='Full name', copyOfFullName='Full name']
Additional references
Post-converter example. java - ModelMapper: transferring attribute from root to each elements of a list - Stack Overflow.
Answer.
I am trying to serialise and deserialise a Java class using Jackson and the JsonPropertyOrder depends on the value of the version field in the class. If version = 1, I want order to be {"start1", "start2"}, if version = 2, order should be {"end1", "end2"}.
I have below class:
#Builder
#Value
#AllArgsConstructor(onConstructor=#__(#JsonCreator))
#JsonPropertyOrder(custom property order depending on version field)
public class ClassA {
#NonNull Integer version;
#NonNull String start1;
#NonNull String start2;
#NonNull String end1;
#NonNull String end2;
}
How can I define the JsonPropertyOrder based on version on runtime. If I should use a custom Deserializer, I cannot figure out how exactly it should be implemented and set with the ObjectMapper.
This is the code for deserialisation:
private final ObjectMapper objectMapper = new ObjectMapper();//have initialzed this as a bean
String jsonStr = "{\"version\":1, \"startLat\":\"47.6812\", \"startLng\":\"-122.3268\", \"endLat\":\"47.6074\", \"endLng\":\"-122.3377\"}";
ClassA objA = null;
try {
objA = objectMapper.readValue(jsonStr, ClassA.class);
} catch (IOException e) {
log.error("Error deserializing the string", jsonStr, e);
}
return objA;
**EDIT: I missed an imp part. The property order here matters because the serialised string might not have the field names. Is that possible to do?
So the str [1, "47.6812", "-122.3268"] will need to be deserialised to the fields version, start1, start2.
If these properties are exclusive why do not create more concise POJO by removing two of them? You can use knowledge that 1 means something different than 2 by introducing some isMethod-es or Enum. To serialise class as JSON array you need to use #JsonFormat(shape = JsonFormat.Shape.ARRAY) annotation. To deserialise you can use 3-arg constructor with #JsonCreator and #JsonProperty annotations. Properties are final. All together makes - class is well implemented. See example:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Objects;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
ClassA classA1 = new ClassA(1, "Start_1", "Start_2");
ClassA classA2 = new ClassA(2, "End_1", "End_2");
String json1 = mapper.writeValueAsString(classA1);
String json2 = mapper.writeValueAsString(classA2);
System.out.println(json1);
System.out.println(json2);
System.out.println(mapper.readValue(json1, ClassA.class));
System.out.println(mapper.readValue(json2, ClassA.class));
}
}
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
class ClassA {
private final Integer version;
private final String value1;
private final String value2;
#JsonCreator
public ClassA(#JsonProperty("version") Integer version,
#JsonProperty("start1") String value1,
#JsonProperty("start2") String value2) {
Objects.requireNonNull(version);
if (!(version == 1 || version == 2)) {
throw new IllegalArgumentException("Version is not supported!");
}
this.version = version;
this.value1 = value1;
this.value2 = value2;
}
#JsonIgnore
public boolean isStart() {
return version == 1;
}
#JsonIgnore
public boolean isEnd() {
return version == 2;
}
public Integer getVersion() {
return version;
}
public String getValue1() {
return value1;
}
public String getValue2() {
return value2;
}
#Override
public String toString() {
return "ClassA{" +
"version=" + version +
", value1='" + value1 + '\'' +
", value2='" + value2 + '\'' +
", isEnd()='" + isEnd() + '\'' +
", isStart()='" + isStart() + '\'' +
'}';
}
}
Above code prints:
[1,"Start_1","Start_2"]
[2,"End_1","End_2"]
ClassA{version=1, value1='Start_1', value2='Start_2', isEnd()='false', isStart()='true'}
ClassA{version=2, value1='End_1', value2='End_2', isEnd()='true', isStart()='false'}
I have a list of objects where a few records can have empty value property and a few can have null value property. Using Collectors.groupingBy I need both the records to be considered as same.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class Code {
private String type;
private String description;
public static void main(String[] args) {
List<Code> codeList = new ArrayList<>();
Code c = new Code();
c.setDescription("abc");
c.setType("");
codeList.add(c);
Code c1 = new Code();
c1.setDescription("abc");
c1.setType(null);
codeList.add(c1);
Map<String, List<Code>> codeMap = codeList.stream()
.collect(Collectors.groupingBy(code -> getGroupingKey(code)));
System.out.println(codeMap);
System.out.println(codeMap.size());
}
private static String getGroupingKey(Code code) {
return code.getDescription() +
"~" + code.getType();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The result of codeMap will have two records since it considers the empty string and the null value in the Type property as different. How can I achieve getting a single record here by considering both the null and empty records as same.
You can modify your getGroupingKey method like this:
private static String getGroupingKey(Code code) {
return code.getDescription() + "~" + (code.getType() == null ? "" : code.getType());
}
or like this:
private static String getGroupingKey(Code code) {
return code.getDescription() + "~" + Optional.ofNullable(code.getType()).orElse("");
}
or you might as well modify your getType() method directly as in:
public String getType() {
return type == null ? "" : type;
}
or:
public String getType() {
return Optional.ofNullable(type).orElse("");
}
Either should work the same. Pick one depending on your requirements I guess..
If you add the following toString method to your Code class:
#Override
public String toString() {
return "Code{" +
"type='" + type + '\'' +
", description='" + description + '\'' +
'}';
}
.. with the modified getGroupingKey method (or the getType method) the output should be as follows:
{abc~=[Code{type='', description='abc'}, Code{type='null', description='abc'}]}
1
Edit: You can also considering initializing the type to an empty String instead of null, then you would not need to modify anything:
private String type = "";
That might be an option too..
I have a string which looks like this and it represents a pojo.
Model [Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989 ]
I want bit clearer to reader on the above string. I want to read the user checked checkbox values(represents entire row with the fileds in below pojo) in an jsp page table to another jsp page. So, in the controller i read these checked checkbox rows as bellow.
String[] checkeditems = request.getParameterValues("case");//case represents the entire row
for (String string : checkeditems) {
log.info("row1"+string);// String pasted above in the message
}
From the above it returns as a string Array which i want convert to be as a list object, so that i can easily send this list to next jsp for a view. I feel i am heading to wrong direction and doing some unrelated stuff.
I have a pojo as
public class Model{
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
====Getters/Setter======
How i can convert this String to this model object?
you can split the string on ", " and iterate over the result array. With BeanUtils from apache can you fill your new pojo instance.
Example:
public class Model {
private String Name;
private String location;
private String actualTransferDate;
private String scanserialCode;
private String modelNum;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getActualTransferDate() {
return actualTransferDate;
}
public void setActualTransferDate(String actualTransferDate) {
this.actualTransferDate = actualTransferDate;
}
public String getScanserialCode() {
return scanserialCode;
}
public void setScanserialCode(String scanserialCode) {
this.scanserialCode = scanserialCode;
}
public String getModelNum() {
return modelNum;
}
public void setModelNum(String modelNum) {
this.modelNum = modelNum;
}
#Override
public String toString() {
return "[Name = " + getName() + "location = " +getLocation() + ", actualTransferDate = " + getActualTransferDate() + ", scanserialCode = " + getScanserialCode() + ", modelNum = " + getModelNum() + "]";
}
}
import org.apache.commons.beanutils.BeanUtils;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
String model = new String("Name=Mobie , location= US, actualTransferDate=null, scanserialCode=234335,1237787, modelNum=MIC 898989");
String[] modelValues = model.split(", ");
Model m = new Model();
for (String value : modelValues) {
String[] s = value.split("=");
String fieldName = s[0];
String fieldValue = s[1];
BeanUtils.setProperty(m, fieldName, fieldValue);
}
System.out.println(m.toString());
}
}
Maven dependency:
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
If you want it completely dynamic, you can use Reflection.
For example, use a regular expression (Pattern/Matcher) to find the [ ... ] part, use the String before that as a class name (assuming you know the package name) and then do a simple comma/equals-sign split in the [ ... ] part and fill the fields via reflection... Not that hard to do.
You can define a constructor in the Model class which accepts the full string as input. The use StringTokenizer with delimiter as ',' to convert the string to a list of tokens. Then tokenizer each token with '='as the delimiter. This way you will have all the members of Model class tokens which can be used to initialize the values of the member variables.
I have a lots of classes that extends from one class
Also I have one method that its argument is that parent class and create query base on attribute of those classes.
sometimes I need to ignore some attribute from result query.
so is it possible to remove some attribute of object?
class A1 extends Model {
public String field1 = "";
public String field2 = "";
public String table = "A1";
#Override
public String getTable() {
return this.table;
}
}
class A2 extends Model {
public String field1 = "";
public String field2 = "";
public String field3 = "";
public String table = "A2";
#Override
public String getTable() {
return this.table;
}
}
class Utility {
public static String query(Model params) {
Field[] fields = params.getClass().getFields();
String head = "INSERT INTO " + params.getTable() + "(";
String tail = "VALUES (";
for(Field field : fields) {
String key = field.getName();
String val;
try {
val = field.get(params);
} catch (Exception e) {
val = null;
}
head += key + ",";
tail += "'" + val + "',";
}
head = head.substring(head,0,head.length() -1) + ")";
tail = tail.substring(tail,0,tail.length() -1) + ")";
return head + tail;
}
}
I call query method by sending one model
A1 data = new A1();
data.field1 = "Name";
data.field2 = "Family";
String query = Utility.query(data);
I just want to remove field2 from query how can I do that?
thanks for any help
You could implement an annotiation. Let's call it #DontPersist. Use it to mark fields which should not get persisted. In Utility.query() you can check for the annotation with reflection.
As your Model class does not implement anything (it could be an interface, but that's another topic), you can extend it creating a class with less attributes when necessary (an anonymous class will do the job).
Anyway, I think you should refactor your code: why not using a List to store fields? It's easier and it does not need reflection.
I'll use something like:
public class Model extends ArrayList{
public Model(String name) { tableName=name;}
private String tableName;
public String getTable() {return tableName}
}
And then you can iterate over the Array to obtain the field names.