XML фида Java с привязкой пространства имен с использованием JAXB - PullRequest
0 голосов
/ 25 июня 2019

Я пытаюсь связать элемент электронной почты в приведенном ниже примере XML-файла, но не могу связать его должным образом.Я получаю нулевое значение.

Пример XML

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
<api:schema-version>5.3</api:schema-version>
<category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
<id>tag:elements@abcd,5.15:/proted-api/v5.5/feeds/users/12312</id>
  <entry>
   <id>tag:elements@abcd,5.15:/proted-api/v5.5/users/12312</id>
   <category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
   <content type="xhtml">
   <api:object category="user" id="12312" proprietary-id="abcd123">
      <api:last-name>Jo</api:last-name>
      <api:first-name>Deo</api:first-name>
      <api:email-address>jode@example.com</api:email-address>
    </api:object>
  </entry>
</feed>

Feed.java

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {

@XmlElement(name="entry")
private Entry entry;

public Entry getEntry() {
    return entry;
}

public void setEntry(Entry entry) {
    this.entry = entry;
}
}

Entry.java

@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {

@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private Object object;

public Object getObject() {
    return object;
}

public void setObject(Object object) {
    this.object = object;
}
}

Object.java

@XmlAccessorType(XmlAccessType.FIELD)
public class Object {

@XmlElement(name="email-address",namespace = "http://www.example.com/publications/api")
private String email;

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

}

Тестовый код

//code here to read
Feed feed = response.getBody();
System.out.println("object email = " +feed.getEntry().getObject().getEmail());

Полный XML-файл

    <feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
    <api:schema-version>5.5</api:schema-version>
    <category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
    <id>tag:elements@Test,5.15:/secure-api/v5.5/feeds/users/676</id>
    <updated>2019-06-17T09:04:39.87+01:00</updated>
    <generator uri="https://test.com/" version="5.15">Example Elements</generator>
    <icon>https://test.com:8091/secure-api/v5.5/Example.ico</icon>
    <rights>This data is the property of the Organisation, and can only be used with permission.</rights>
    <subtitle>This feed represents a single user.</subtitle>
    <link type="application/atom+xml" rel="self" href="https://test.com:8091/secure-api/v5.5/users/676" />
    <title>John deo</title>
    <author>
    <name>Example Elements at Test PROD</name>
    </author>
    <entry>
    <id>tag:elements@Test,5.15:/secure-api/v5.5/users/676</id>
    <category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
    <updated>2019-06-17T09:04:39.87+01:00</updated>
    <link type="application/atom+xml" rel="alternate" href="https://test.com:8091/secure-api/v5.5/users/676" />
    <title>John deo</title>
    <content type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml">
    <p>User</p>
    <a href="https://test.com:8091/secure-api/v5.5/users/676/photo?type=profile">Photo</a>
    <p>
    <a href="https://test.com:8091/secure-api/v5.5/users/676/relationships">Relationships</a>
    with other data
    </p>
    </div>
    </content>
    <api:object category="user" id="676" proprietary-id="abcd1247" username="abcd1247" last-affected-when="2019-06-17T09:04:39.87+01:00" last-modified-when="2018-11-29T10:28:43.403+00:00" href="https://test.com:8091/secure-api/v5.5/users/676" created-when="2010-05-04T09:49:46.507+01:00" type-id="1" type="person">
    <!-- User type 1 is "person" -->
    <api:ever-approved>true</api:ever-approved>
    <api:is-public>false</api:is-public>
    <api:is-login-allowed>true</api:is-login-allowed>
    <api:title>Prof</api:title>
    <api:initials>Jo</api:initials>
    <api:last-name>John</api:last-name>
    <api:first-name>Deo</api:first-name>
    <api:email-address>John.deo@example.com</api:email-address>
    <api:known-as>Liz</api:known-as>
    <api:primary-group-descriptor>ABCS</api:primary-group-descriptor>
    <api:arrive-date>2009-10-05</api:arrive-date>
    <api:user-search-settings>
    <api:default>
    </api:default>
    </api:user-search-settings>
    <api:records>
    <api:record format="native" id="3245433412" source-id="1" source-name="manual" source-display-name="Manual">
    <api:native />
    </api:record>
    </api:records>
    <api:fields />
    <api:relationships href="https://test.com:8091/secure-api/v5.5/users/676/relationships" />
    <api:user-identifier-associations user-id="676" proprietary-id="abcd1247" username="abcd1247">
    </api:user-identifier-associations>
    </api:object>
    </entry>
    </feed>

1 Ответ

2 голосов
/ 25 июня 2019

Проблема в том, что ваша структура POJO не соответствует xml (и, как мы выяснили в конце, RestTemplate не использовал аннотации JAXB, см. Редактирование 4 ниже).

Давайте исправим это, чтобы вы могли правильно выполнить демаршаллинг.

Большинство полей используют пространство имен http://www.w3.org/2005/Atom. Мы будем использовать информацию о пакете вместо того, чтобы добавлять ее в каждый элемент. Поэтому создайте файл package-info.java в пакете ваших классов с содержимым:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.w3.org/2005/Atom", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package the.package.of.your.classes;

Тогда давайте исправим подачу. У вашего xml есть элементы, которых нет у вашего класса, и это проблема, потому что он встречает неожиданные элементы. Подача будет:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {

    @XmlElement(name="schema-version", namespace = "http://www.example.com/publications/api")
    private String schemaVersion;
    @XmlElement
    private String category;
    @XmlElement
    private String id;

    @XmlElement(name="entry")
    private Entry entry;

}

Обратите внимание, что версия схемы имеет другое пространство имен, чем то, что мы имеем в информации о пакете, поэтому мы явно переопределяем ее в аннотациях.

Теперь это упрощено. Например, категория имеет атрибуты xml, например, термин и метка. Если вы хотите получить эту информацию, вы должны создать класс для представления Category вместо String. Но если вы этого не сделаете, Лента, подобная этой, позволяет нам продолжать демаршаллинг. Следующая запись:

@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {


    @XmlElement
    private String id;
    @XmlElement
    private String category;
    @XmlElement
    private String content;

    @XmlElement(name="object", namespace = "http://www.example.com/publications/api")
    private ApiObject object;

}

Снова атрибуты здесь игнорируются с использованием String. Я изменил Object на класс с именем ApiObject, структура которого соответствует xml. Это выглядит так:

@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {

    @XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
    private String lastName;
    @XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
    private String firstName;
    @XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
    private String emailAddress;
}

И, наконец, xml. Недопустимый xml, тег «content» не закрыт в вставленном образце. Я изменил его на <content type="xhtml" />, чтобы проверить мое решение.

И действительно, это работает и отлично разбирается :) 1023 *

РЕДАКТИРОВАТЬ, чтобы ответить:

Для вопроса 1 вы бы добавили пространство имен в каждый элемент, который прямо не указывает пространство имен.

Для вопроса 2 Вы можете игнорировать неизвестные элементы, используя то, что было предложено здесь: JAXB Игнорировать «дополнительные» элементы из XML-ответа

Лента, например, будет выглядеть так:

@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {

    @XmlAnyElement(lax = true)
    private List<Object> anything;

    @XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
    private Entry entry;


}

РЕДАКТИРОВАТЬ 2:

Возьмите xml, который вы пытаетесь разобрать, и сделайте что-то вроде ниже:

try {
            File file = new File("/path/to/your/file.xml");
            JAXBContext jaxbContext = JAXBContext.newInstance(YouRootClass.class);

            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            jaxbUnmarshaller.setEventHandler(
                    new ValidationEventHandler() {
                        public boolean handleEvent(ValidationEvent event ) {
                            throw new RuntimeException(event.getMessage(),
                                    event.getLinkedException());
                        }
                    });
            YouRootClass pojo = (YouRootClass) jaxbUnmarshaller.unmarshal(file);
        } catch (JAXBException e) {
            e.printStackTrace();
        }

Какое бы несоответствие между вашими xml и POJO не возникло, вы можете исправить это

РЕДАКТИРОВАТЬ 3:

Поток

@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {

    @XmlAnyElement(lax = true)
    private List<Object> anything;

    @XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
    private Entry entry;

}

запись

@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {


    @XmlAnyElement(lax = true)
    private List<Object> anything;

    @XmlElement(name="object", namespace = "http://www.example.com/publications/api")
    private ApiObject object;

}

ApiObject:

@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {

    @XmlAnyElement(lax = true)
    private List<Object> anything;

    @XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
    private String lastName;
    @XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
    private String firstName;
    @XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
    private String emailAddress;
}

РЕДАКТИРОВАТЬ 4:

В вашем проекте github класс Test после создания RestTemplate (строка 31) добавляет:

List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
messageConverters.add(jaxb2RootElementHttpMessageConverter);
rest.setMessageConverters(messageConverters);
...