Auch wenn ich den Inhalt eines Datenobjekts in Java in JSON konvertieren möchte, gibt es einen Zirkelverweis ...

Was ist ein "Datenobjekt" in diesem Artikel?

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.

Umgebung

Was du machen willst

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"
}

Konvertieren Sie mit ReflectionToString

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.

Erstellen wir eine Basisklasse für DTO

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"
}

Achten Sie auf Zirkelverweise!

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.

Unterstützt auch Zirkelreferenz

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!

Zusammenfassung

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

Auch wenn ich den Inhalt eines Datenobjekts in Java in JSON konvertieren möchte, gibt es einen Zirkelverweis ...
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben
[Java] Ich möchte mit dem Schlüssel im Objekt eindeutig arbeiten
[Rails] Ich möchte Daten verschiedener Modelle in einem Formular senden
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (PowerMockito Edition)
Ich möchte den Inhalt der Absicht var_dump
Sogar in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (Javassist zweite Abkochung)
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 (Black Magic) ausgeben.
[Rails] Ich möchte das Linkziel von link_to auf einer separaten Registerkarte anzeigen
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (graue Magie, die weniger schwarze Magie ist)
Selbst in Java möchte ich true mit == 1 && a == 2 && a == 3 ausgeben (Royal Road Edition, die weder Magie noch irgendetwas ist)
Ich möchte die MD5-Prüfsumme einer Datei in Java finden und das Ergebnis als Zeichenfolge in hexadezimaler Notation erhalten.
[Java] Ich möchte ein Byte-Array in eine Hexadezimalzahl konvertieren
Ich habe versucht, in Java von einer Zeichenfolge in einen LocalDate-Typ zu konvertieren
Ich möchte eine Parkettdatei auch in Ruby erstellen
Ich habe einen RESAS-API-Client in Java erstellt
Ich möchte die if-else-Anweisung für bedingte Verzweigungen in Java vereinfachen
Ich möchte herausfinden, welche Java-Version die JAR-Datei hat, die ich habe
Ich möchte das Flash-Attribut im Frühjahr, auch wenn ich einen Reverse-Proxy festgelegt habe! (TU es nicht)
Ich möchte eine Liste des Inhalts einer Zip-Datei und ihrer unkomprimierten Größe erhalten
Ich möchte JSON-Daten (Objekte) mit Ajax zwischen Java und JavaScript austauschen! ~ Frühlingsausgabe ~
Selbst wenn ich die Einstellung von STRICT_QUOTE_ESCAPING in CATALINA_OPTS in tomcat8.5 schreibe, wird sie nicht wiedergegeben.
Ich möchte den Inhalt von Assets in der mit capistrano erstellten Umgebung von Grund auf neu erstellen
Wenn in Ruby Hash [: a] [: b] [: c] = 0 ist, möchten wir, dass Sie rekursiv erweitern, auch wenn der Schlüssel nicht vorhanden ist
[Swift] Wenn Sie wissen möchten, ob die Anzahl der Zeichen in String mit einer bestimmten Anzahl übereinstimmt ...
Informationen zur Methode zum Konvertieren einer Zeichenfolge in eine Ganzzahl / einen Bruch (Umwandlungsdaten) in Java
Die in /lib/calendars.properties von Java jre festgelegte Millisekunde ist UTC
Ich möchte den Rahmen des Textfelds rot machen, wenn ein Eingabefehler auftritt
Statische Funktion, um zu überprüfen, ob der RGB-Fehler von BufferdImage innerhalb des angegebenen Verhältnisses in Java liegt
So überprüfen Sie den Inhalt der Java-Zeichenfolge mit fester Länge
Ich möchte den Wert von Attribute in Selenium of Ruby ändern
[Für Anfänger] Ich möchte mit einem Auswahlbefehl automatisch vorregistrierte Daten in das Eingabeformular eingeben.
[Java] Geben Sie das Ergebnis von ffprobe -show_streams in JSON aus und ordnen Sie es einem Objekt in Jackson zu
Ich habe versucht, ein übergeordnetes Wertklasseobjekt in Ruby zu erstellen
Die Geschichte, zu vergessen, eine Datei in Java zu schließen und zu scheitern
Anmerkung: [Java] Wenn sich eine Datei im überwachten Verzeichnis befindet, verarbeiten Sie sie.
Ich möchte die IP-Adresse erhalten, wenn ich mit Java eine Verbindung zu Wi-Fi herstelle
So ermitteln Sie den absoluten Pfad eines in Java ausgeführten Verzeichnisses
Ich möchte für jedes Array mit Lambda-Ausdruck in Java
Ich möchte den Feldnamen des [Java] -Felds erhalten. (Alter Ton)
Android-Entwicklung, wie man den Wert des JSON-Objekts auf null überprüft
[Rails] Ich möchte alles zurücksetzen, weil die Daten in der lokalen Umgebung falsch sind! Was ist vorher zu tun?
Wenn Sie die Testabdeckung privater Methoden in JUnit erfüllen möchten
Ich habe die Daten der Reise (Tagebuchanwendung) in Java erhalten und versucht, sie # 001 zu visualisieren
Stellen Sie sicher, dass der JSON des Schlangenfalls dem Feld des Kamelfalls in Java (JVM) entspricht.
[Java] Ist es nicht erforderlich, "Identität" bei der Implementierung der equals () -Methode zu überprüfen?
So erstellen Sie eine eindeutige Datenkombination in der Schienen-Zwischentabelle
Ich habe es geschafft, ein Leerzeichen zu bekommen, als ich den Inhalt von Beans in den Textbereich gebracht habe
Ich möchte den Inhalt der Anfrage sehen, ohne vier oder fünf zu sagen
Ich möchte rekursiv die Oberklasse und die Schnittstelle einer bestimmten Klasse erhalten
Ich möchte eine E-Mail in Java senden.
Code zum Escapezeichen von JSON-Zeichenfolgen in Java
Spüren Sie den Lauf der Zeit auch in Java
Ich wollte (a == 1 && a == 2 && a == 3) in Java wahr machen
rsync4j - Ich möchte rsync in Java berühren.
Ich möchte irgendwann sogar in Kotlin sein