10847


JavaのJsonパーサJacksonを使って配列をパースするとき普通であれば。以下のようにレスポンスのモデルを作成し、`readValues`を用いてパースできます。

[code]

    ObjectMapper mapper = new ObjectMapper();
    try{
      ResponseClass response = mapper.readValue(response.toString(), ResponseClass.class);
    }(SomeException e){
      }
    
    --------------
    
    //Model class
    class ResponseClass{
      public List<SomeType> items;
    }
    
    --------------
    
    //Json response
    {
      [
         {
            type:"TypeA",
            name:"hoge"
         },
      ........
    
[/code]

しかし例えばこの配列の要素の型が複数ある場合はどうすればいいのでしょうか。例えば取得するJsonオブジェクトが以下のようなものの場合。

[code]

    {
      [
        {
          type:"typeA",
          name:"hoge"
        },
        {
          type:"typeB",
          mail:"hoge@hoge.com"
        }
       .......
      ]
    {
    
[/code]

この場合はObjectモデルのListのジェネリクスとフィールド名はどのようにすればいいのでしょう。


CC BY-SA 3.0
2015-05-30T12:59:25.977
10798
2018-04-20T01:33:41.780
2017-08-04T12:30:11.643
13199
7232
question
java json jackson
Jacksonで2つ以上の型を持つ配列をパースするには


JsonArrayに変換したあとで、for文を使ってその要素を調べて特定の要素がある場合で場合分けしてからJsonObjectにパースしました。  
他にもいい方法があれば教えていただけると幸いです。


CC BY-SA 3.0
2015-06-01T12:40:50.473
10847
2015-06-01T12:40:50.473
7232
10798
answer


JsonTypeInfoとJsonSubTypesを使うことで一度のパースでobjectに変換できます。

以下のようなクラスの形になります。

[code]

    import com.fasterxml.jackson.annotation.JsonSubTypes;
    import com.fasterxml.jackson.annotation.JsonTypeInfo;
    
    import java.io.Serializable;
    
    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Hero.SpiderMan.class, name = "spiderMan"),
            @JsonSubTypes.Type(value = Hero.IronMan.class, name = "ironMan")
    })
    public abstract class Hero implements Serializable {
        private static final long serialVersionUID = -1043995422704061066L;
    
        public static class SpiderMan extends Hero {
            private static final long serialVersionUID = 3086739225292748194L;
    
            private String realmName;
    
            public String getRealmName() {
                return realmName;
            }
    
            public void setRealmName(String realmName) {
                this.realmName = realmName;
            }
        }
    
        public static class IronMan extends Hero {
            private static final long serialVersionUID = -235208732212962783L;
    
            private String height;
    
            public String getHeight() {
                return height;
            }
    
            public void setHeight(String height) {
                this.height = height;
            }
        }
    }
    
[/code]

JsonTypeInfoでどうやってクラスを判別するか という情報をJsonに埋め込みます。  
僕の書いたコードの場合だと、 type というフィールドをプロパティとして埋め込みます。  
また、JsonSubTyoesは実際のクラスを記述します。

以下はテストしたコード

[code]

    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.testng.annotations.Test;
    
    import java.io.IOException;
    import java.io.Serializable;
    import java.util.LinkedList;
    import java.util.List;
    
    public class HeroTest {
        private static final String SERIALIZED_JSON = "{\"heroes\":[{\"type\":\"spiderMan\",\"realmName\":\"Peter Benjamin Parker\"},{\"type\":\"ironMan\",\"height\":\"6'1\\\"; (in armor) 6'6\\\"\"}]}";
    
        @Test
        public void shouldBeSerialized() throws JsonProcessingException {
            Hero.SpiderMan spiderMan = new Hero.SpiderMan();
            spiderMan.setRealmName("Peter Benjamin Parker");
    
            Hero.IronMan ironMan = new Hero.IronMan();
            ironMan.setHeight("6'1\"; (in armor) 6'6\"");
    
            List<Hero> heros = new LinkedList<>();
            heros.add(spiderMan);
            heros.add(ironMan);
    
            Mervel mervel = new Mervel();
            mervel.setHeroes(heros);
    
            ObjectMapper objectMapper = new ObjectMapper();
            String serialized = objectMapper.writeValueAsString(mervel);
    
            assert SERIALIZED_JSON.equals(serialized);
        }
    
        @Test
        public void shouldBeObject() throws IOException {
            ObjectMapper objectMapper = new ObjectMapper();
            Mervel mervel = objectMapper.readValue(SERIALIZED_JSON, Mervel.class);
    
            for (Hero hero : mervel.heroes) {
    
                if (hero instanceof Hero.SpiderMan) {
                    System.out.println("I am spider man. Real name is " +((Hero.SpiderMan) hero).getRealmName());
                } else if (hero instanceof Hero.IronMan) {
                    System.out.println("I am Iron man. Height is " +((Hero.IronMan) hero).getHeight());
                } else {
                    System.err.println("An error occurred... :(");
                }
            }
        }
    
        private static class Mervel implements Serializable {
            private static final long serialVersionUID = -5640686224279032240L;
    
            private List<Hero> heroes;
    
            public List<Hero> getHeroes() {
                return heroes;
            }
    
            public void setHeroes(List<Hero> heroes) {
                this.heroes = heroes;
            }
        }
    }
    
[/code]


CC BY-SA 3.0
2018-04-09T03:58:54.243
43084
2018-04-20T01:33:41.780
2018-04-20T01:33:41.780
24823
24823
10798
answer