[JAVA] Umgang mit Elementen, von denen Sie nicht wissen, ob sie in GSON einzeln oder als Array vorliegen

Geschichte

Als ich den JSON mit der Redmine REST-API erhielt, wurde die folgende Notation in "custom_fields" angezeigt.

"custom_fields": [
  {
    "id": 1,
    "name": "Gegenstand 1",
    "value": "a"
  },
  {
    "id": 2,
    "name": "Punkt 2",
    "multiple": true,
    "value": [
      "b"
    ]
  },
  {
    "id": 3,
    "name": "Punkt 3",
    "multiple": true,
    "value": [
      "c",
      "d"
    ]
  },

Es scheint, dass wenn der Inhalt von "Wert" "mehrfach": wahr "ist, es ein Array-Objekt ist, andernfalls ist es einzeln. Wie im obigen Beispiel gibt es 3 Muster.

  1. Einzelstück (Mehrfach nicht angegeben)
  2. Die Anzahl der Elemente im Array beträgt eins
  3. Die Anzahl der Elemente im Array beträgt 2 oder mehr

Es gibt auch eine Methode, um alle in ein Array zu konvertieren, selbst wenn es zum Zeitpunkt der Deserialisierung einzeln ist (JSON → Java). Da es jedoch zum Zeitpunkt der Serialisierung nicht auf den ursprünglichen Ausdruck zurückgesetzt werden kann (ich weiß nicht, ob es sich um ein einzelnes Array oder ein Array mit 1 Element handelt), setzen Sie dieses Steuerelement In diesem Sinne habe ich auch eine allgemeine Implementierung in Betracht gezogen.

Implementierung

Datenobjekt

Die Objekte, die zum Zeitpunkt der Deserialisierung Daten speichern sollen, sind wie folgt.

public class MultipleType<T> {

	private T value;

	private List<T> values;

	public boolean isMultiple() {
		return values != null;
	}

	//Unter Getter/setter

}

Verwenden Sie entweder Wert oder Werte und machen Sie den anderen Null.

Die obigen "custom_fields" sehen wie folgt aus.

public class CustomField {
	private String id;
	private String name;
	private boolean multiple;
	private MultipleType<String> value;

	//Unter Getter/setter
}

Ich möchte die Funktionen von GSON so oft wie möglich nutzen, daher werde ich den gleichen Feldnamen wie JSON verwenden.

Serializer / Deserializer

Implementieren Sie den benutzerdefinierten Serializer / Deserializer von GSON wie folgt.

public class MultipleTypeAdapter implements JsonDeserializer<MultipleType<?>>, JsonSerializer<MultipleType<?>> {

	@Override
	public MultipleType<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
			throws JsonParseException {

		final MultipleType<?> result = new MultipleType<>();

		if (json.isJsonArray()) {
			result.setValues(deserializeArray(json, typeOfT, context));
		} else {
			result.setValue(context.deserialize(json, getGenericType(typeOfT)));
		}
		return result;
	}

	private <T> List<T> deserializeArray(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
		final List<T> values = new ArrayList<>();
		final Type t = getGenericType(typeOfT);
		for (JsonElement e : json.getAsJsonArray()) {
			values.add(context.deserialize(e, t));
		}
		return values;
	}

	/* get actual Type of <?> */
	private Type getGenericType(Type typeOfT) {
		return ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
	}

	@Override
	public JsonElement serialize(MultipleType<?> src, Type typeOfSrc, JsonSerializationContext context) {
		return context.serialize(src.isMultiple() ? src.getValues() : src.getValue());
	}

}

Es gibt drei Hauptpunkte.

  1. Verwenden Sie beim Deserialisieren "isJsonArray ()", um zu überprüfen, ob es sich um ein Array handelt, und wechseln Sie den Prozess.
  2. Rufen Sie den Typparameter "MultipleType" ab
  3. Verwenden Sie zum Zeitpunkt der Serialisierung "isMultiple ()", um zu überprüfen, ob es sich um ein Array handelt, und wechseln Sie den Prozess.

Besonders 2 ist etwas kompliziert. Zunächst müssen wir wissen, was das Argument "Type typeOfT" der Methode "deserialize" darstellt. Es enthält ein Objekt, das den Typparameter der JsonDeserializer-Schnittstelle darstellt. (Um genau zu sein, der Typ des Objekts, das zur Laufzeit tatsächlich verwendet wird) Generische Typparameter gehen zur Kompilierungszeit verloren, sodass Sie zur Laufzeit auf sie verweisen können.

Die Definition sagt uns, dass "typeOfT" "MultipleType " Ist. Da wir jedoch den "" - Teil kennen müssen, erhalten wir die internen Typparameter mit der Methode "getGenericType". Ich werde.

Der Grund, warum der Laufzeit-Typ des Typparameters "<?>" Erforderlich ist, besteht darin, dass die nachfolgende Deserialisierung der Funktion von GSON überlassen werden kann, wenn der richtige Typ durch die Methode "context.deserialize" angegeben wird. .. Insbesondere müssen Sie keine komplexe Strukturdeserialisierung von Hand schreiben, wenn die Felder des Datenobjekts vom nicht-primitiven Typ sind (einschließlich Zeichenfolge).

Wo GSON verwendet wird

Die Verwendung von GSON ist wie folgt.

final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MultipleType.class, new MultipleTypeAdapter());
final Gson gson = gsonBuilder.create();

Da die Klasse "MultipleTypeAdapter" kein generischer Typ ist, ist es ein Fehler, dass keine Warnung angezeigt wird. Aus diesem Grund haben wir "<?>" (Platzhalter) für die benutzerdefinierten Deserializer / Serializer-Typparameter verwendet. (Ich kann nicht erklären, warum Platzhalter in Ordnung sind ...)

Apropos

Ich benutze den generischen Java-Typ als "generisch" in Sätzen und die Aussprache als "generisch". Ist es nicht schwierig, "Generika" zu sagen?

Recommended Posts

Umgang mit Elementen, von denen Sie nicht wissen, ob sie in GSON einzeln oder als Array vorliegen
So ersetzen Sie Zeichen, die Sie nicht verstehen [Prinzip]
So verhindern Sie, dass Benutzer, die nicht angemeldet sind, zur neuen Seite oder zur Bearbeitungsseite wechseln
[Ruby] Wie man gerade oder ungerade Zahlen in einem Array zählt
So bestimmen Sie, ob Sie in PagerView nach links oder rechts gewechselt haben
Was tun, wenn die Änderungen im Servlet nicht berücksichtigt werden?
So führen Sie eine Basiskonvertierung in Java durch
Was tun, wenn die Änderungen nicht in der JAR-Manifestdatei berücksichtigt werden?
10 Sperrfeuer mit ● oder ■, die wahrscheinlich im Training auftreten (Java)