Почему я не могу сопоставить этот простой объект с текстом в XML на Java / Jersey? - PullRequest
3 голосов
/ 27 октября 2019

У меня есть REST API, созданный с помощью Джерси на Java. Для одного запроса я хотел бы вернуть в JSON список кортежей пары координат. Для этого у меня есть класс, который является оберткой для ArrayList, Tuple2 класса и Coords класса. Я использую javax.xml.bind.annotations для автоматической генерации XML / JSON моих классов.

Но по причине, по которой я не понимаю, мой Coords класс не может быть привязан к XML.

Я пробовал разные типы атрибутов (Integers вместо int), имея @XmlAttribute в разных местах (перед атрибутами и перед получателями) и разные XmlAccessType (PROPERTY вместо NONE) но результаты были одинаковыми.

Вот мой класс 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;
    }
}

А вот как он присутствует в моем 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;
    }
// ...
}

Вот сообщение об ошибке:

[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.

Спасибо за помощь.

1 Ответ

2 голосов
/ 27 октября 2019

Ваша проблема связана с неправильным использованием аннотаций xml. Вы определяете Tuple2 как корневой элемент xml, аннотируя его с помощью @XmlRootElement, а его поля - как атрибуты xml, аннотируя методы get с помощью @XmlAttribute. Что означает:

<tuple2 first="first_attributes_vale" second="second_attributes_value" />

Теперь оба поля имеют тип Coords, который объявлен как другой элемент xml, аннотируя класс Coords с помощью @XmlRootElement, а его поля - xml. атрибутов. Когда Coords будет сериализован в xml, это будет:

<coords x="value" y="value" />

Проблема возникает при сериализации Tuple2. Его поля должны быть атрибутами xml, но Coords - это еще один элемент xml. Атрибуты XML не могут содержать вложенные элементы, а только значения.

Решение

В зависимости от того, что вы хотите, вы можете решить это двумя различными способами. Хотя я бы не рекомендовал второй подход, потому что он странный (хотя он и работает) и потребует дополнительных усилий на стороне клиента (см. Пояснения ниже).

Первый подход

Аннотируйте методы getFirst() и getSecon() с помощью аннотации @XmlElement.

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

Это даст результат, который выглядит следующим образом:

<tuple2>
    <first x="2" y="4"/>
    <second x="12" y="12"/>
</tuple2>

Второй подход

Это странный способ его решения. Это работает, но требует дополнительных усилий на стороне клиента, потому что значения Coords кодируются как строковые значения и требуют анализа на принимающей стороне.

Измените тип возврата getFirst() и getSecond() методов для String и переопределения toString() метода 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();
    }
}

Переопределения toString() метода 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();
    }
}

Thisвыдаст результат, подобный следующему:

<tuple2 first="Coords [x=2, y=4]" second="Coords [x=12, y=12]"/>

Значения атрибутов first и second будут такими, какие возвращает toString() метод Coords.

...