Pourquoi ne puis-je mapper ce simple objet au texte en XML en Java / Jersey?

hforrat:

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 Tuple2classe et une Coordsclasse. J'utilise le javax.xml.bind.annotationspour générer automatiquement le XML / JSON de mes cours.

Mais pour une raison que je ne comprends pas ma Coordsclasse ne peut pas me être mis en correspondance XML.

J'ai essayé différents types d'attributs ( au Integerslieu de int), ayant la @XmlAttributeà différents endroits (avant les attributs et avant les getters) et différents XmlAccessType( au PROPERTYlieu 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.

ahoxha:

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 Coordsclasse @XmlRootElement, et ses champs à attributs XML. Quand Coordsobtient 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 Coordsest 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' @XmlElementannotation.

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 Coordssont 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 Stringet 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 firstet secondseront quelle que soit la toString()méthode des Coordsrendements.

Je suppose que tu aimes

Origine http://43.154.161.224:23101/article/api/json?id=331098&siteId=1
conseillé
Classement