HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Не JSON-ом единым оперируют back-end-ы приложения. В дикой природе встречаются Web-сервисы, которые на работают с XML (да, мы не о SOAP, а именно об XML). SpringMVC прекрасно поддерживает данную возможность, причём разными способами.</p>
1 <p>Не JSON-ом единым оперируют back-end-ы приложения. В дикой природе встречаются Web-сервисы, которые на работают с XML (да, мы не о SOAP, а именно об XML). SpringMVC прекрасно поддерживает данную возможность, причём разными способами.</p>
2 <h2>JSON c Jackson</h2>
2 <h2>JSON c Jackson</h2>
3 <p>По умолчанию spring-boot-starter-web содержит настроенный Jackson для маппинга request/response DTO в JSON.</p>
3 <p>По умолчанию spring-boot-starter-web содержит настроенный Jackson для маппинга request/response DTO в JSON.</p>
4 <p>Раз:</p>
4 <p>Раз:</p>
5 &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt; &lt;/dependency&gt;<p>(включает в себя jackson-databind)</p>
5 &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt; &lt;/dependency&gt;<p>(включает в себя jackson-databind)</p>
6 <p>Два:</p>
6 <p>Два:</p>
7 @RequiredArgsConstructor @Getter public class PersonJsonDto { @JsonProperty("firstName") private final String name; private final int age; }<p>Три:</p>
7 @RequiredArgsConstructor @Getter public class PersonJsonDto { @JsonProperty("firstName") private final String name; private final int age; }<p>Три:</p>
8 @RestController public class PersonJsonController { @GetMapping("/api/json") public PersonJsonDto json() { return new PersonJsonDto("Ivan", 18); } }<p>Данный контроллер содержит аннотацию @RestController, которая содержит @ResponseBody, которая в свою очередь говорит Spring MVC преобразовывать объект, используя существующие мапперы. Да, по умолчанию подразумевается application/json, для которого уже есть настроенный Jackson.</p>
8 @RestController public class PersonJsonController { @GetMapping("/api/json") public PersonJsonDto json() { return new PersonJsonDto("Ivan", 18); } }<p>Данный контроллер содержит аннотацию @RestController, которая содержит @ResponseBody, которая в свою очередь говорит Spring MVC преобразовывать объект, используя существующие мапперы. Да, по умолчанию подразумевается application/json, для которого уже есть настроенный Jackson.</p>
9 <p>Проверяем:</p>
9 <p>Проверяем:</p>
10 GET http://localhost:8080/api/json HTTP/1.1 200 Content-Type: application/json ... { "age": 18, "firstName": "Ivan" }<h2>XML c Jackson</h2>
10 GET http://localhost:8080/api/json HTTP/1.1 200 Content-Type: application/json ... { "age": 18, "firstName": "Ivan" }<h2>XML c Jackson</h2>
11 <p>Jackson, на самом деле, не “библиотека для маппинга в JSON”, а полноценный фреймворк для маппинга объектов не только в JSON, но и в XML, YAML и даже в бинарных форматах.</p>
11 <p>Jackson, на самом деле, не “библиотека для маппинга в JSON”, а полноценный фреймворк для маппинга объектов не только в JSON, но и в XML, YAML и даже в бинарных форматах.</p>
12 <p>Подобные расширения Jacson называются dataformat.</p>
12 <p>Подобные расширения Jacson называются dataformat.</p>
13 <p>Подключим соответствующую JAR:</p>
13 <p>Подключим соответствующую JAR:</p>
14 &lt;dependency&gt; &lt;groupId&gt;com.fasterxml.jackson.dataformat&lt;/groupId&gt; &lt;artifactId&gt;jackson-dataformat-xml&lt;/artifactId&gt; &lt;/dependency&gt;<p>В лучших традициях Spring Boot эта библиотека сразу будет настроена spring-boot-starter-mvc.</p>
14 &lt;dependency&gt; &lt;groupId&gt;com.fasterxml.jackson.dataformat&lt;/groupId&gt; &lt;artifactId&gt;jackson-dataformat-xml&lt;/artifactId&gt; &lt;/dependency&gt;<p>В лучших традициях Spring Boot эта библиотека сразу будет настроена spring-boot-starter-mvc.</p>
15 <p>Напишем соответствующий DTO:</p>
15 <p>Напишем соответствующий DTO:</p>
16 @JacksonXmlRootElement( namespace = "http://otus.ru/spring", localName = "person" ) @RequiredArgsConstructor @Getter public class PersonXmlDto { private final String name; private final int age; }<p>Да, стоит отметить, что c маппингом XML не всё так просто. Теперь поля объекта могут быть как вложенными тегами, так и атрибутами. И не стоит забывать про namespaces элементов. Пример, представленный здесь, не лишён недостатков, поэтому искушённым читателям предлагается прочитать<a>документацию</a>.</p>
16 @JacksonXmlRootElement( namespace = "http://otus.ru/spring", localName = "person" ) @RequiredArgsConstructor @Getter public class PersonXmlDto { private final String name; private final int age; }<p>Да, стоит отметить, что c маппингом XML не всё так просто. Теперь поля объекта могут быть как вложенными тегами, так и атрибутами. И не стоит забывать про namespaces элементов. Пример, представленный здесь, не лишён недостатков, поэтому искушённым читателям предлагается прочитать<a>документацию</a>.</p>
17 <p>И напишем контроллер:</p>
17 <p>И напишем контроллер:</p>
18 @RestController public class PersonXmlController { @GetMapping( value = "/api/jackson-xml", produces = "application/xml" ) public PersonXmlDto jacksonXml() { return new PersonXmlDto("Ivan", 18); } }<p>Обратите внимание на параметр аннотации produces - здесь мы задали application/xml, чтобы сработал именно XML-маппер.</p>
18 @RestController public class PersonXmlController { @GetMapping( value = "/api/jackson-xml", produces = "application/xml" ) public PersonXmlDto jacksonXml() { return new PersonXmlDto("Ivan", 18); } }<p>Обратите внимание на параметр аннотации produces - здесь мы задали application/xml, чтобы сработал именно XML-маппер.</p>
19 <p>Если мы ничего не напишем, то значение по умолчанию - application/json и будет действовать именно Jackson JSON dataformat, как в предыдущем примере.</p>
19 <p>Если мы ничего не напишем, то значение по умолчанию - application/json и будет действовать именно Jackson JSON dataformat, как в предыдущем примере.</p>
20 <p>Кстати, если мы будем принимать XML на вход с помощью @RequestBody, то нам необходимо будет указать уже consumes.</p>
20 <p>Кстати, если мы будем принимать XML на вход с помощью @RequestBody, то нам необходимо будет указать уже consumes.</p>
21 <p>Проверяем:</p>
21 <p>Проверяем:</p>
22 GET http://localhost:8080/api/jackson-xml HTTP/1.1 200 Content-Type: application/xml ... &lt;person xmlns="http://otus.ru/spring"&gt; &lt;name xmlns=""&gt;Ivan&lt;/name&gt; &lt;age xmlns=""&gt;18&lt;/age&gt; &lt;/person&gt;<h2>JAXB и Spring MVC</h2>
22 GET http://localhost:8080/api/jackson-xml HTTP/1.1 200 Content-Type: application/xml ... &lt;person xmlns="http://otus.ru/spring"&gt; &lt;name xmlns=""&gt;Ivan&lt;/name&gt; &lt;age xmlns=""&gt;18&lt;/age&gt; &lt;/person&gt;<h2>JAXB и Spring MVC</h2>
23 <p>Говоря об XML в Java, часто подразумевают именно JAXB. JAXB исключена из стандартной поставки Java 11, поэтому добавим вместо jackson-dataformat-xml зависимость:</p>
23 <p>Говоря об XML в Java, часто подразумевают именно JAXB. JAXB исключена из стандартной поставки Java 11, поэтому добавим вместо jackson-dataformat-xml зависимость:</p>
24 &lt;dependency&gt; &lt;groupId&gt;org.glassfish.jaxb&lt;/groupId&gt; &lt;artifactId&gt;jaxb-runtime&lt;/artifactId&gt; &lt;/dependency&gt;<p>DTO выглядит похожим образом:</p>
24 &lt;dependency&gt; &lt;groupId&gt;org.glassfish.jaxb&lt;/groupId&gt; &lt;artifactId&gt;jaxb-runtime&lt;/artifactId&gt; &lt;/dependency&gt;<p>DTO выглядит похожим образом:</p>
25 @XmlRootElement( namespace = "http://spring.otus.ru", name = "person-jaxb" ) @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class PersonJaxbDto { private String name; private int age; }<p>И, как мы уже знаем, необходимо написать соответствующим образом контроллер:</p>
25 @XmlRootElement( namespace = "http://spring.otus.ru", name = "person-jaxb" ) @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class PersonJaxbDto { private String name; private int age; }<p>И, как мы уже знаем, необходимо написать соответствующим образом контроллер:</p>
26 @RestController public class PersonJaxbController { @GetMapping( value = "/api/jaxb-xml", produces = "application/xml" ) public PersonJaxbDto jacksonXml() { return new PersonJaxbDto("Ivan", 18); } }<p>И, наконец, пробуем!</p>
26 @RestController public class PersonJaxbController { @GetMapping( value = "/api/jaxb-xml", produces = "application/xml" ) public PersonJaxbDto jacksonXml() { return new PersonJaxbDto("Ivan", 18); } }<p>И, наконец, пробуем!</p>
27 GET http://localhost:8080/api/jaxb-xml Content-Type: application/xml ... &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt; &lt;ns2:person-jaxb xmlns:ns2="http://spring.otus.ru"&gt; &lt;age&gt;18&lt;/age&gt; &lt;name&gt;Ivan&lt;/name&gt; &lt;/ns2:person-jaxb&gt;<p>Весь код доступен по<a>ссылке</a>. Успехов!</p>
27 GET http://localhost:8080/api/jaxb-xml Content-Type: application/xml ... &lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt; &lt;ns2:person-jaxb xmlns:ns2="http://spring.otus.ru"&gt; &lt;age&gt;18&lt;/age&gt; &lt;name&gt;Ivan&lt;/name&gt; &lt;/ns2:person-jaxb&gt;<p>Весь код доступен по<a>ссылке</a>. Успехов!</p>
28  
28