You may want to combine the contents of an object into a character string and output it to a log. At that time, I don't want to log this field! Is there something like that? It's a bit annoying to implement toString (), isn't it?
Cart.java
package jp.co.pmtech.iwata;
public class Cart {
/**Cart ID*/
private int id;
/**Items in the cart*/
private List<Syohin> syohinList;
//getter or settert
}
Syohin.java
package jp.co.pmtech.iwata;
public class Syohin {
/**Product ID*/
private int id;
/**Product name*/
private String name;
/**price*/
private BigDecimal price;
//getter or settert
}
App.java
public final class App {
public static void main(String[] args) {
Syohin syohin1 = new Syohin();
syohin1.setId(1);
syohin1.setName("Ballpoint pen");
syohin1.setPrice(new BigDecimal(120));
Syohin syohin2 = new Syohin();
syohin2.setId(2);
syohin2.setName("tissue");
syohin2.setPrice(new BigDecimal(298));
List<Syohin> list = new ArrayList<>();
list.add(syohin1);
list.add(syohin2);
Cart cart = new Cart();
cart.setId(1);
cart.setSyohinList(list);
}
}
It can be realized by implementing as follows.
By the way, RecursiveToStringStyle has been increased from commons-lang3.2, and you can use it to recursively convert it to a string. However, since ReflectionToStringBuilder is called internally, the field to be output cannot be controlled. Therefore, I decided to extend ToStringStyle by myself this time. (Details of RecursiveToStringStyle will be described later in the bonus part)
LogIgnore.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogIgnore {
}
This time, I will try not to give the price of the product (Syohin # price).
Syohin.java
public class Syohin {
/**Product ID*/
private int id;
/**Product name*/
private String name;
/**price*/
@LogIgnore
private BigDecimal price;
}
It seems that ReflectionToStringBuilder # accept () determines the field to be stringified, so override this.
LoggingToStringBuilder.java
public class LoggingToStringBuilder extends ReflectionToStringBuilder {
public LoggingToStringBuilder(final Object object, final ToStringStyle style) {
super(object, style);
}
public static String reflectionToString(final Object object, final ToStringStyle style) {
LoggingToStringBuilder builder = new LoggingToStringBuilder(object, style) {
@Override
protected boolean accept(final Field field) {
if (!super.accept(field)) {
return false;
}
if (field.getAnnotation(LogIgnore.class) != null) {
return false;
}
return true;
}
};
return builder.toString();
}
}
Before commons-lang3.2, it seems that you made it yourself.
Create a class that extends ToStringStyle by referring to the following article. (Since the name happens to be commons-lang, I created it with the class name LoggingToStringStyle this time.) Recursive output with reflectionToString-A Memorandum
You can copy and paste the class, but please modify only the following points.
LoggingToStringStyle.java
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
for(String packaz : recursivePackags) {
if (value.getClass().getCanonicalName().startsWith(packaz)) {
// buffer.append(ToStringBuilder.reflectionToString(value, this));
//↓ Change here
buffer.append(LoggingToStringBuilder.reflectionToString(value, this));
return;
}
}
buffer.append(value);
}
App.config
String str = LoggingToStringBuilder.toString(cart, new LoggingToStringStyle("jp.co.pmtech.iwata"));
System.out.println(str);
result
jp.co.pmtech.iwata.Cart[id=1,syohinList=[jp.co.pmtech.iwata.Syohin[id=1,name=Ballpoint pen],jp.co.pmtech.iwata.Syohin[id=2,name=tissue]]]
The contents of Cart and Syohin are correct, and the price is not displayed.
The purpose of this time is to output to the log, so I don't care about the output format. Let's display the JSON version using Jackson. If you add the JsonIgnore annotation, it will not be subject to JSON serialization.
Syohin.java
public class Syohin {
/**Product ID*/
private int id;
/**Product name*/
private String name;
/**price*/
@JsonIgnore
private BigDecimal price;
}
App.java
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(cart);
System.out.println(str);
result
{"id":1,"syohinList":[{"id":1,"name":"Ballpoint pen"},{"id":2,"name":"tissue"}]}
I can't get the price value.
There is only one problem, it seems that Spring Boot's Rest Controller is converted to JSON with Jackson. I didn't use this method because I wanted to include price in the API return. If you are not using Jackson, you can use this method.
As mentioned above, RecursiveToStringStyle has been added since 3.2 of commons-lang. Let's do it for a moment. Put the following code at the end of main and execute it.
App.java
String str = ReflectionToStringBuilder.toString(cart, new RecursiveToStringStyle());
System.out.println(str);
result
jp.co.pmtech.iwata.Cart@610455d6[id=1,syohinList=java.util.ArrayList@2c7b84de{jp.co.pmtech.iwata.Syohin@5a07e868[id=1,name=Ballpoint pen,price=java.math.BigDecimal@36baf30c[intVal=<null>,scale=0]],jp.co.pmtech.iwata.Syohin@76ed5528[id=2,name=tissue,price=java.math.BigDecimal@24d46ca6[intVal=<null>,scale=0]]}]
It seems that it is recursively converted to a character string, but the object ID is displayed and it is a little noisy. Moreover, the price of Big Decimal has not come out ...
It's a good use, but maybe it's just a log spit out ... For security reasons, you don't want to spit out personal information and passwords in the log. Also, is it a very large object that you want to log? ??
Depending on the requirements, JSON or Commons-lang RecursiveToStringStyle is fine. The source code is posted on github, so please refer to it. https://github.com/pmt-iwata/LoggingToStringBuilder
Recommended Posts