Теперь GlassFish 4 предлагает полную платформу Java EE 7 (JSR-342) . EclipseLink сделал несколько важных вкладов в этот выпуск. Первый обеспечивает реализацию JPA 2.1 (JSR-338) . Второе, о чем я расскажу в этом посте, — EclipseLink MOXy — теперь поставщик JSON-привязки по умолчанию для приложений JAX-RS.
RESTful Service
Обслуживание клиентов
Обычно реальный сервис будет поддерживаться JPA для выполнения операций персистентности (см. Создание веб-сервиса RESTful — часть 4/5 ). Но для этого поста я буду использовать «Hello World»
служба стиля, которая возвращает Customer на основе идентификатора в виде XML и JSON, чтобы проиллюстрировать некоторые моменты, связанные с привязкой
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package org.example.service;import javax.ejb.*;import javax.ws.rs.*;import javax.ws.rs.core.MediaType;import org.example.model.*;@Stateless@LocalBean@Path("/customers")public class CustomerService { @GET @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Path("{id}") public Customer read(@PathParam("id") int id) { Customer customer = new Customer(); customer.setId(id); customer.setName("Jane Doe"); PhoneNumber pn = new PhoneNumber(); pn.setType("work"); pn.setValue("5551111"); customer.getPhoneNumbers().add(pn); return customer; }} |
CustomerApplication
Я использовал класс Application, чтобы указать путь к сервису.
|
1
2
3
4
5
6
7
8
9
|
package org.example.service;import javax.ws.rs.ApplicationPath;import javax.ws.rs.core.Application;@ApplicationPath("rest/*")public class CustomerApplication extends Application {} |
Модель Java
Ниже приведена модель Java, которую мы будем использовать для этого примера. Целью является создание хороших представлений XML и JSON с использованием единого набора метаданных.
Клиент
Есть несколько интересных вещей, которые стоит отметить в классе Customer .
- Свойство id — это int . Поскольку JSON имеет другое представление для чисел и текста, мы посмотрим, как это значение будет представлено в выводе JSON.
- Свойство phoneNumbers имеет тип List <PhoneNumber> . Он был аннотирован @XmlElementWrapper для получения хорошего вывода XML. Мы рассмотрим влияние этого на вывод JSON.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package org.example.model;import java.util.*;import javax.xml.bind.annotation.*;@XmlRootElementpublic class Customer { private int id; private String name; private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElementWrapper @XmlElement(name="phoneNumber") public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; }} |
Номер телефона
В XML класс PhoneNumber отображается на сложный тип с простым содержимым (см. JAXB и Сложные типы с простым содержимым ). Это означает, что он отображается на элемент XML с атрибутами и текстом. Эти концепции XML не соответствуют непосредственно концепциям JSON, поэтому мы рассмотрим представление JSON.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package org.example.model;import javax.xml.bind.annotation.*;public class PhoneNumber { private String type; private String value; @XmlAttribute public String getType() { return type; } public void setType(String type) { this.type = type; } @XmlValue public String getValue() { return value; } public void setValue(String value) { this.value = value; }} |
Использование Сервиса
Запрос — ПОЛУЧИТЬ
Ниже приведен URL, который мы будем использовать для доступа к нашему сервису:
|
1
|
http://localhost:8080/CustomerResource/rest/customers/1 |
Ответ (приложение / XML)
Поскольку мы сопоставили нашу модель домена с
Метаданные JAXB (JSR-222), представленные ниже в формате XML, не являются неожиданностью.
|
1
2
3
4
5
6
7
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><customer id="1"> <name>Jane Doe</name> <phoneNumbers> <phoneNumber type="work">5551111</phoneNumber> </phoneNumbers></customer> |
Ответ (приложение / JSON) — GlassFish 3.1.2
Давайте взглянем на то, как выглядело представление JSON в GlassFish 3.1.2 до того, как MOXy был поставщиком JSON-привязки по умолчанию. Есть несколько вещей, на которые следует обратить внимание:
- Значение int для свойства id представляется в виде текста JSON.
- Свойство phoneNumbers типа List <PhoneNumber>
поскольку у него был один элемент, он был представлен как объект JSON вместо массива JSON. Также, даже если этот ключ содержит коллекцию, он все еще называется phoneNumber . - Ключ JSON, соответствующий свойству типа, поскольку он был аннотирован @XmlAttribute, имеет префикс @ .
- Ключ JSON, соответствующий свойству value, поскольку он аннотирован @XmlValue , называется $ .
|
01
02
03
04
05
06
07
08
09
10
|
{ "id": "1", "name": "Jane Doe", "phoneNumbers": { "phoneNumber": { "@type": "work", "$": "5551111" } }} |
Ответ (приложение / JSON) — GlassFish 4
Используя конфигурацию по умолчанию, вы увидите, что обновление до GlassFish 4 устраняет две самые большие проблемы:
- Числовые значения — теперь свойство id правильно маршалируется в JSON как числовое значение. Это не уловка, MOXy основывает представление JSON на типе свойства Java. Свойство value типа String в экземпляре PhoneNumber содержит только цифры, и оно правильно отображается в JSON как текст.
- Коллекции размера 1 — ключ phoneNumber теперь является массивом JSON, к сожалению, он по-прежнему называется phoneNumber (я покажу, как это исправить).
|
01
02
03
04
05
06
07
08
09
10
|
{ "id": 1, "name": "Jane Doe", "phoneNumbers": { "phoneNumber": [ "@type": "work", "$": "5551111" ] }} |
Настройка JSON-Binding
MOXy теперь можно настроить, используя механизм JAX-RS ContextResolver . Вам просто нужно вернуть экземпляр MOXyJsonConfig . Мы будем использовать это, чтобы сделать следующее:
- Укажите, что мы не хотим ставить префикс JSON-ключей, соответствующих свойствам, сопоставленным с @XmlAttribute .
- Используйте значение ключа JSON вместо $ для свойств, сопоставленных с @XmlValue .
- MOXyJsonConfig также может использоваться для передачи свойств маршаллеру / маршаллеру. Мы сделаем это, чтобы очистить ключ JSON для свойств коллекции (см .: Привязка к JSON и XML — Обработка коллекций ).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package org.example.service;import javax.ws.rs.ext.*;import org.eclipse.persistence.jaxb.JAXBContextProperties;import org.glassfish.jersey.moxy.json.MoxyJsonConfig;@Providerpublic class MOXyJsonContextResolver implements ContextResolver<MoxyJsonConfig> { private final MoxyJsonConfig config; public MOXyJsonContextResolver() { config = new MoxyJsonConfig() .setAttributePrefix("") .setValueWrapper("value") .property(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); } @Override public MoxyJsonConfig getContext(Class<?> objectType) { return config; }} |
Новый ответ
Наконец, у нас есть JSON-представление, которым мы можем быть довольны. Он не содержит никаких артефактов, связанных с XML, даже если он был создан с теми же метаданными, что и представление XML (которое не изменилось). Все это заняло немного МОКСИ.
|
01
02
03
04
05
06
07
08
09
10
|
{ "id": 1, "name": "Jane Doe", "phoneNumbers": [ { "type": "work", "value": "5551111" } ]} |