Bezieht sich auf eine Java-Klasse, die zum Speichern von Daten definiert ist. Es ist eine Klasse, die nur "private Mitgliedsvariable + Getter & Setter" oder öffentliche Mitgliedsvariable hat. Ich mache es oft, wenn ich Daten aus einer Datenbank mit Web-API oder JPA abrufe. Manchmal auch "DTO" genannt.
Selbst wenn es ein solches Datenobjekt gibt, gibt es Zeiten, in denen Sie die hier gespeicherten Daten in ein Protokoll oder dergleichen ausgeben möchten.
public class TestDto
{
public String stringvar;
public Integer integervar;
public int intvar;
public TestDto2 testDto2;
public BigDecimal decimal;
public java.util.Date date;
}
public class TestDto2
{
public String string2;
public Integer integer2;
public int int2;
public TestDto testDto;
}
Wenn Sie in einem solchen Fall in JSON wie ↓ ausgeben können, ist dies sehr leicht zu erkennen.
{
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"string2": "CCC",
"integer2": 2,
"int2": 0,
"testDto": null
},
"decimal": null,
"date": "2020-08-12T00:00:00.000+0900"
}
Apache Commons'ToStringBuilder # ReflectionToString` war schon immer bekannt. Dies ist eine praktische Methode, bei der mithilfe von Reflektion der Inhalt eines Datenobjekts in eine Zeichenfolge konvertiert wird. Tatsächlich kann das Ausgabeformat im zweiten Argument angegeben werden, und JSON wird ebenfalls ordnungsgemäß vorbereitet.
ToStringBuilder.reflectionToString(dto, ToStringStyle.JSON_STYLE)
↓ Ausgabeergebnis (separat hübsch gedruckt)
{
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": "jp.example.common.dto.TestDto2@ed17bee",
"decimal": null,
"date": "Wed Aug 12 00:00:00 JST 2020"
}
Hmmm, sorry ...
Bei verschachtelten Datenobjekten werden nur der Klassenname und der Hashwert wie "testDto2": "jp.example.common.dto.TestDto2@ed17bee", "angezeigt, und die Daten können nicht angezeigt werden.
ToStringBuilder # ReflectionToString
verwendet toString ()
, um den Inhalt jedes Elements in eine Zeichenfolge zu konvertieren. Wenn also die übergeordnete Klasse nicht wie dieses Mal angegeben wird, wird Object # toString
verwendet und ↑ Das Ausgabeergebnis sieht folgendermaßen aus.
Dann erstellen wir eine Basisklasse für ein Datenobjekt wie ↓.
import org.apache.commons.lang3.builder.ToStringBuilder;
public class CommonDto implements Serializable
{
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}
Stellen Sie sicher, dass das Datenobjekt von der Klasse ↑ erbt.
public class TestDto extends CommonDto
{
public String stringvar;
public Integer integervar;
public int intvar;
public TestDto2 testDto2;
public BigDecimal decimal;
public java.util.Date date;
}
public class TestDto2 extends CommonDto
{
public String string2;
public Integer integer2;
public int int2;
public TestDto testDto;
}
In diesem Fall können Sie in JSON nur mit toString ()
ausgeben.
dto.toString();
↓ Ausgabeergebnis (separat hübsch gedruckt)
{
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"string2": "CCC",
"integer2": 2,
"int2": 0,
"testDto": null
},
"decimal": null,
"date": "Wed Aug 12 00:00:00 JST 2020"
}
Es fühlt sich perfekt an, aber es gibt eine Falle. Wenn ein Mitglied in einem Datenobjekt auf sein übergeordnetes Element verweist. Wenn Sie der Referenz gehorsam folgen, können Sie sich für immer darauf beziehen.
Wenn Sie versuchen, auf "testDto2.testDto", das im obigen Beispiel null ist, als übergeordnetes Element zu verweisen ...
{
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"string2": "CCC",
"integer2": 2,
"int2": 0,
"testDto": {
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"string2": "CCC",
"integer2": 2,
"int2": 0,
"testDto": {
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"string2": "CCC",
"integer2": 2,
"int2": 0,
"testDto": {
:
Es schleift unendlich so. Ich glaube nicht, dass Sie normalerweise so darauf verweisen würden, aber wenn Sie eine solche Entität mit JPA erstellen und miteinander in Beziehung setzen, wird genau ein solches Objekt zurückgegeben. Selbst wenn Sie sich den Stapelüberlauf ansehen, können Sie einige Personen sehen, die Probleme mit diesem Problem haben. Es sieht aus wie eine Falle, die unerwartet leicht zu begegnen ist.
Da der Zweck dieser Zeit die Protokollausgabe ist, möchte ich sie nutzbar machen, ohne mir Gedanken darüber zu machen, ob es sich um eine Zirkelreferenz handelt oder nicht.
(Wenn der Zweck beispielsweise eine REST-API-Antwort ist, glaube ich nicht, dass das von JPA erhaltene Objekt so beantwortet wird, wie es ist, sodass Sie sich keine Gedanken über das Zirkelreferenzproblem machen müssen.)
Solange ich "ToStringStyle.JSON_STYLE" verwende, dachte ich, ich könnte keine Zirkelverweise verarbeiten. Deshalb werde ich dieses Mal versuchen, eine Originalklasse zu erstellen, die "ToStringStyle.JSON_STYLE" sehr ähnlich sieht.
Die Substanz von "ToStringStyle.JSON_STYLE" ist "JsonToStringStyle", die als innere Klasse von "ToStringStyle" definiert ist. Kopieren Sie sie also zuerst vollständig und speichern Sie sie in Ihrem Projekt.
↓ So (wie JsonToStringStyle
mit Ausnahme des Klassennamens)
public class OriginalJsonToStringStyle extends ToStringStyle {
private static final long serialVersionUID = 1L;
private static final String FIELD_NAME_QUOTE = "\"";
/**
* <p>
* Constructor.
* </p>
*
* <p>
* Use the static constant rather than instantiating.
* </p>
*/
OriginalJsonToStringStyle() {
super();
this.setUseClassName(false);
this.setUseIdentityHashCode(false);
:
Dann schreibt nur die ↓ -Methode den Inhalt neu.
Eigentlich hat ToStringStyle
ursprünglich einen Mechanismus, um Zirkelverweise richtig zu behandeln, aber die Verarbeitung nach der Methode von ↓ ist etwas chaotisch und diese Verarbeitung wird durchlaufen. Aus diesem Grund befand es sich in einer Endlosschleife.
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (value == null) {
appendNullText(buffer, fieldName);
return;
}
if (value instanceof String || value instanceof Character) {
appendValueAsString(buffer, value.toString());
return;
}
if (value instanceof Number || value instanceof Boolean) {
buffer.append(value);
return;
}
final String valueAsString = value.toString();
if (isJsonObject(valueAsString) || isJsonArray(valueAsString)) {
buffer.append(value);
return;
}
appendDetail(buffer, fieldName, valueAsString);
}
↓ Ich habe es so geändert
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
if (value == null) {
appendNullText(buffer, fieldName);
return;
}
if (value instanceof String || value instanceof Character) {
appendValueAsString(buffer, value.toString());
return;
}
if(value instanceof java.util.Date)
{
appendValueAsDate(buffer, (java.util.Date)value);
return;
}
buffer.append(value);
}
//Übrigens Java.util.Ausgabedatum im erweiterten ISO8601-Format
protected void appendValueAsDate(StringBuffer buffer, java.util.Date value) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
buffer.append("\"" + df.format(value) + "\"");
}
Nachdem wir uns mit Zirkelverweisen vertraut gemacht haben, fügen wir die folgende Methode hinzu: Bei einem Objekt, auf das zirkulär verwiesen wird, wird die in ↑ umgeschriebene normale Stringifizierungsmethode nicht verwendet und von dieser Methode verarbeitet. Der Ausgabeinhalt kann beliebig sein, daher entspricht das Ergebnis von "ObjectUtils # identityToString" dem der Override-Quellmethode. Vorher und nachher werden jedoch doppelte Anführungszeichen hinzugefügt, um das JSON-Format nicht zu beschädigen.
@Override
protected void appendCyclicObject(final StringBuffer buffer, final String fieldName, final Object value) {
buffer.append(FIELD_NAME_QUOTE);
ObjectUtils.identityToString(buffer, value);
buffer.append(FIELD_NAME_QUOTE);
}
↓ Ausgabeergebnis (separat hübsch gedruckt)
{
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"testDto2": {
"int2": 0,
"integer2": 2,
"string2": "CCC",
"testDto": {
"stringvar": "aaa",
"integervar": 1,
"intvar": 2,
"date": "2020-08-12T00:00:00.000+0900",
"decimal": null,
"testDto2": "jp.example.common.dto.TestDto2@5577140b"
}
}
"date": "2020-08-12T00:00:00.000+0900",
"decimal": null
}
Gute Stimmung! Der Teil, auf den zirkulär verwiesen wird, wird doppelt ausgegeben, aber durch die Notation "jp.example.common.dto.TestDto2@5577140b" ersetzt und ist nicht in eine Endlosschleife geraten!
Das Konvertieren eines Datenobjekts in JSON ist mit "ToStringBuilder # ReflectionToString" möglich. Wenn jedoch zirkulär auf das Objekt verwiesen wird, z. B. bei Verwendung von JPA, musste eine spezielle Verarbeitungsklasse erstellt werden.
Apropos JSON-Konvertierung: Neben ToStringBuilder gibt es GSON und Jackson (ObjectMapper), aber die Objekte, auf die zirkulär verwiesen wird, sind immer noch nutzlos.
Recommended Posts