12

I'm using the dependency:

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
    </dependency>

I have the following objects:

public Class X {

  private String a;
  private String b;
  private Y y;
}

public Class Y {
  private String c;
  private String d;
}

And I need to log the content of the class X recursively to get the class Y as well and using the JSON style. That is my goal and this is the purpose of this question.

Approach 1:

ToStringStyle style = ToStringStyle.JSON_STYLE;
LOGGER.debug(ReflectionToStringBuilder.toString(new X(), style, false, false));

Approach 1 result:

{"a":"<a>","b":"<b>","y": com.<packages>.y@<hash>}

Approach 2:

RecursiveToStringStyle style = new RecursiveToStringStyle();
LOGGER.debug(ReflectionToStringBuilder.toString(new X(), style, false, false));

Approach 2 result:

[com.<packages>.x@<hash>[a=<a>,b=<b>,y=com.<packages>.y@<hash>[c=<c>,d=<d>]]

Successful Approach:

`Merge of Approach 1 and Approach 2` but how to achieve this? 

Successful Approach result (My goal):

{"a":"<a>","b":"<b>","y":{"c":"<c>","d":"<d>"}}
f_puras
  • 2,521
  • 4
  • 33
  • 38
Leonel
  • 2,796
  • 5
  • 25
  • 36
  • Sounds like a feature request you could file... Could be implemented as a subclass of RecursiveToStringStyle, similar to the existing MultilineRecursiveToStringStyle. – Thilo Nov 06 '15 at 11:43
  • Yes, I already try this approach. But I think there must be a better way to achieve my goal. Maybe using another dependency? – Leonel Nov 06 '15 at 11:49
  • FYI, this Question and these Answers seem to apply to `commons-lang3` version `3.9` as well. – daveloyall Oct 25 '19 at 19:10

3 Answers3

4

I found the solution. I need to Override the method toString of each classes (X and Y in this case)

public Class X {

  private String a;
  private String b;
  private Y y;

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
  }
}

public Class Y {
  private String c;
  private String d;

  @Override
  public String toString() {
    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
  }
}

And now, with the approach 1 It's working.

Leonel
  • 2,796
  • 5
  • 25
  • 36
  • 2
    Sure, that would work, if you can change the `toString` methods of all involved classes. The nice thing about the ToStringBuilder is that it can be external. But why make things more complicated than necessary. – Thilo Nov 06 '15 at 13:05
2

You can extend the RecursiveToStringStyle class to write your own style class and set the parameters to match the JSON_STYLE or any other style for that matter.

class CustomStyle extends RecursiveToStringStyle {

    public CustomStyle() {
        super();
        super.setUseClassName(false);
        super.setUseIdentityHashCode(false);
        super.setContentStart("{");
        super.setContentEnd("}");
        super.setArrayStart("[");
        super.setArrayEnd("]");
        super.setFieldSeparator(",");
        super.setFieldNameValueSeparator(":");
        super.setNullText("null");
        super.setSummaryObjectStartText("\"<");
        super.setSummaryObjectEndText(">\"");
        super.setSizeStartText("\"<size=");
        super.setSizeEndText(">\"");
    }
}

public class Z {
    public String objectToString(Object obj) {
        ToStringStyle style = new CustomStyle();
        return new ReflectionToStringBuilder(obj, style).toString();
    }
}

To create a custom style, a good point will be to look at the style parameters set in the ToStringStyle class. You can use the setter methods to customize the default settings in your custom implementation.

tkruse
  • 10,222
  • 7
  • 53
  • 80
  • 1
    Note: This approach will output values that feel like JSON, but which do not parse as JSON. In particular, it does not quote field names or string values. This is because String-quoting is implemented as part of the JsonToStringStyle class, in method called appendValueAsString(). – Kristian Holdhus Sep 12 '18 at 18:56
0

I had a similar problem and since it was affecting only one field as well, I ended up doing this (I actually use SHORT_PREFIX_STYLE but for the sake of example I keep it as JSON):

@Override
public String toString() {
    return (new ReflectionToStringBuilder(this, ToStringStyle.JSON_STYLE) {
        @Override
        protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException {
            return "y".equals(field.getName()) ?
                ReflectionToStringBuilder.toString(y, ToStringStyle.JSON_STYLE) :
                super.getValue(field);
        }
    }).toString();
}

Another reason is that I don't have control over field y source code.

(It's too bad that instances of ToStringStyle are not designed in some composable way. I think recursion is not really a part of the "style" at all, it's an aspect of traversion which should have been modeled independently)

alexandroid
  • 1,469
  • 2
  • 15
  • 32