J'ai une API REST créé avec Jersey en Java. Pour une demande , je voudrais revenir dans JSON une liste de tuples de paire de coordonnées. Pour cela, j'ai une classe qui est une enveloppe pour une ArrayList
, une Tuple2
classe et une Coords
classe. J'utilise le javax.xml.bind.annotations
pour générer automatiquement le XML / JSON de mes cours.
Mais pour une raison que je ne comprends pas ma Coords
classe ne peut pas me être mis en correspondance XML.
J'ai essayé différents types d'attributs ( au Integers
lieu de int
), ayant la @XmlAttribute
à différents endroits (avant les attributs et avant les getters) et différents XmlAccessType
( au PROPERTY
lieu de NONE
) , mais les résultats étaient les mêmes.
Voici ma classe Coords:
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
}
Et voici comment il est présent dans mon Tuple2
package model;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
// ...
@XmlAttribute
public Coords getFirst() {
return this.c1;
}
@XmlAttribute
public Coords getSecond() {
return this.c2;
}
// ...
}
Voici le message d'erreur:
[EL Warning]: moxy: 2019-10-27 15:01:08.586--javax.xml.bind.JAXBException:
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.
- with linked exception:
[Exception [EclipseLink-50096] (Eclipse Persistence Services - 2.7.4.v20190115-ad5b7c6b2a): org.eclipse.persistence.exceptions.JAXBException
Exception Description: The @XmlAttribute property first in type model.Tuple2 must reference a type that maps to text in XML. model.Coords cannot be mapped to a text value.]
oct. 27, 2019 3:01:08 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
GRAVE: MessageBodyWriter not found for media type=application/json, type=class model.ActionList, genericType=class model.ActionList.
Merci de votre aide.
Votre problème provient de l'utilisation abusive des annotations XML. Vous définissez un Tuple2
être un élément racine xml en annotant avec @XmlRootElement
, et ses champs à attributs xml en annotant les méthodes get avec @XmlAttribute
. Ce qui se traduit par:
<tuple2 first="first_attributes_vale" second="second_attributes_value" />
Maintenant, les deux champs sont de type Coords
, qui est déclaré être un autre élément XML en annotant la Coords
classe @XmlRootElement
, et ses champs à attributs XML. Quand Coords
obtient sérialisé au format XML, ce sera:
<coords x="value" y="value" />
Le problème se produit lorsque sérialisation Tuple2
. Ses champs sont supposés être les attributs xml, mais Coords
est un autre élément XML. Attributs Xml ne peuvent pas contenir des éléments imbriqués, mais seulement des valeurs.
Solution
En fonction de ce que vous voulez, vous pouvez résoudre de deux façons différentes. Bien que, je ne recommanderais pas la deuxième approche, car il est étrange (même si cela fonctionne) et engageront des efforts supplémentaires du côté client (voir les explications ci-dessous).
Première approche
Annoter les getFirst()
et getSecond()
méthodes avec l' @XmlElement
annotation.
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlElement
public Coords getFirst() {
return this.c1;
}
@XmlElement
public Coords getSecond() {
return this.c2;
}
}
Cela produira un résultat qui ressemble à ceci:
<tuple2>
<first x="2" y="4"/>
<second x="12" y="12"/>
</tuple2>
deuxième approche
C'est la façon étrange de le résoudre. Il fonctionne, mais il engage un effort supplémentaire sur le côté client, parce que les valeurs de Coords
sont codées sous forme de chaîne et nécessiteront l' analyse syntaxique du côté de la réception.
Changer le type de retour getFirst()
et des getSecond()
méthodes pour être String
et remplacer la toString()
méthode de Coords
.
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Tuple2 {
private Coords c1;
private Coords c2;
public Tuple2(Coords c1, Coords c2) {
this.c1 = c1;
this.c2 = c2;
}
public Tuple2() {
c1 = new Coords(0, 0);
c2 = new Coords(0, 0);
}
@XmlAttribute
public String getFirst() {
return this.c1.toString();
}
@XmlAttribute
public String getSecond() {
return this.c2.toString();
}
}
Substituer la toString()
méthode de Coords
:
package model;
import static javax.xml.bind.annotation.XmlAccessType.NONE;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(NONE)
public class Coords {
@XmlAttribute private int x;
@XmlAttribute private int y;
public Coords(final int x, final int y) {
this.x = x;
this.y = y;
}
public Coords() {
this.x = 0;
this.y = 0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Coords [x=");
builder.append(x);
builder.append(", y=");
builder.append(y);
builder.append("]");
return builder.toString();
}
}
Cela produira un résultat similaire à ceci:
<tuple2 first="Coords [x=2, y=4]" second="Coords [x=12, y=12]"/>
Les valeurs des attributs first
et second
seront quelle que soit la toString()
méthode des Coords
rendements.