LoginSignup
6
5

More than 5 years have passed since last update.

spring-bootでXMLをコレクションにデシリアライズ

Posted at

概要

spring-bootでHTTPリクエストにXMLを受け取りJavaオブジェクトにデシリアライズする。

ソースコード

pom.xml

pom.xml
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>

デシリアライズ

詳細は以下で後述するが、XMLでJavaのコレクションに対応する部分がXMLのroot直下にあるかどうかで、アノテーションの書き方が変わる。

root直下では無い場合

以下のようなXMLが送られてくる、と想定する。list下に繰り返し要素が存在する。

<sampleRoot>
    <list>
        <sampleElement>
            <id>1</id>
            <value>v1</value>
        </sampleElement>
        <sampleElement>
            <id>2</id>
            <value>v2</value>
        </sampleElement>
    </list> 
</sampleRoot>

この場合は以下のようなクラスを用意する。特にアノテーションも必要ない。

@Data
public class SampleRoot1 {
  List<SampleElement> list;
}
@Data
public class SampleElement {
  String id;
  String value;
}
@RestController
public class SampleController {
  @PostMapping(value = "/hoge1")
  @RequestMapping(consumes = MediaType.TEXT_XML_VALUE)
  public String hoge1(@RequestBody SampleRoot1 s) {
    System.out.println("xml body " + s);
    return "ok";
  }

実行結果は以下のような感じになる。

xml body SampleRoot1(list=[SampleElement(id=1, value=v1), SampleElement(id=2, value=v2)])

root直下の場合

以下のようなXMLが送られてくる、と想定する。root直下に繰り返し要素が存在する。

<sampleRoot>
    <sampleElement>
        <id>1</id>
        <value>v1</value>
    </sampleElement>
    <sampleElement>
        <id>2</id>
        <value>v2</value>
    </sampleElement>
</sampleRoot>

この場合は、以下のように、コレクションに@JacksonXmlElementWrapper(useWrapping = false)を付与する。

package jp.co.rakuten.rmsapi.order.notice.reciever.controller;

import java.util.List;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;

import lombok.Data;

@Data
public class SampleRoot2 {
  @JacksonXmlElementWrapper(useWrapping = false)
  List<SampleElement> sampleElement;
}

ちゃんとドキュメント読んでないんで正確な説明では無いと思うが、useWrapping = falseでこのプロパティは何かの要素で囲まれるものではない、と指定していると思われる。

実行結果。

xml body SampleRoot2(sampleElement=[SampleElement(id=1, value=v1), SampleElement(id=2, value=v2)])

アノテーションが無いと以下のようなエラーになる。SampleElementのインスタンスをList<SampleElement>そのものにマッピングしようとして失敗している感じ。

Failed to resolve argument 0 of type 'jp.co.rakuten.rmsapi.order.notice.reciever.controller.SampleRoot2' 
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `jp.co.rakuten.rmsapi.order.notice.reciever.controller.SampleElement` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `jp.co.rakuten.rmsapi.order.notice.reciever.controller.SampleElement` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')
 at [Source: (PushbackInputStream); line: 3, column: 8] (through reference chain: jp.co.rakuten.rmsapi.order.notice.reciever.controller.SampleRoot2["sampleElement"]->java.util.ArrayList[0])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:241)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223)

なお、XMLの構造によっては以下のように@JacksonXmlRootElement@JacksonXmlPropertyの指定が必要になるケースもあると思われる。また、ぐぐった感じではjacksonのバージョンにも左右されるようなので、そこは都度確認が必要と思われる。

import java.util.List;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

import lombok.Data;

@Data
@JacksonXmlRootElement(localName = "SampleRoot")
public class SampleRoot2 {
  @JacksonXmlElementWrapper(useWrapping = false)
  @JacksonXmlProperty
  List<SampleElement> sampleElement;
}
6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5