web-dev-qa-db-ja.com

Jackson JSON、不変クラス、およびインターフェース

私はJacksonの例で遊んでいますが、不変のクラスとインターフェイスでデシリアライズを機能させるのに問題があります。

以下は私のコードです:

package com.art.starter.jackson_starter;

import Java.io.IOException;
import Java.io.StringReader;
import Java.io.StringWriter;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
/**  * Hello world!  *  */ public class App  {
    public static void main( String[] args ) throws JsonGenerationException, JsonMappingException, IOException
    {
        System.out.println( "Hello World!" );

        AddressImpl.AddressBuilder builder = new AddressImpl.AddressBuilder();
        NameImpl.Builder nameBuilder = new NameImpl.Builder();
        UserImpl.Builder userBuilder = new UserImpl.Builder();


        Name name = nameBuilder.first("FirstName")
                  .last("LastName")
                  .build();

        Address address =  builder.setCity("TestCity")
               .setCountry("TestCountry")
               .setState("PA")
               .setStreet("TestAddress")
               .setZip(123)
               .build();      

        User user = userBuilder.address(address)
                 .gender(User.Gender.MALE)
                 .isVerified(true)
                 .userImage(new byte[5])
                 .build();

        System.out.println(address);        
        System.out.println(name);
        System.out.println(user);

        StringWriter sw = new StringWriter();
        ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(sw, user);
        System.out.println(sw);


       StringReader sr = new StringReader("{\"address\":{\"state\":\"PA\",\"country\":\"TestCountry\",\"street\":\"TestAddress\",\"city\":\"TestCity\",\"Zip\":123},\"verified\":true,\"gender\":\"MALE\",\"userImage\":\"AAAAAAA=\"}");

       /* 
          This line throws the Exception           
       */
       User user2 = mapper.readValue(sr, UserImpl.class);

       System.out.println(user2);
    } }

package com.art.starter.jackson_starter;

import Java.util.Arrays;

import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;

public final class UserImpl implements User
{
   private final Address address;
   private final Gender gender;
   private final byte[] userImage;
   private final boolean isVerified;

   public static class Builder
   {
      private Address address;
      private Gender gender;
      //      private Name name;
      private byte[] userImage;
      private boolean isVerified;

      public Builder address(Address address)
      {
         this.address = address;
         return this;
      }

      public Builder gender(Gender gender)
      {
         this.gender = gender;
         return this;
      }

      //      public Builder name(Name name)
      //      {
      //         this.name = name;
      //         return this;
      //      }

      public Builder userImage(byte[] userImage)
      {
         this.userImage = userImage;
         return this;
      }

      public Builder isVerified(boolean isVerified)
      {
         this.isVerified = isVerified;
         return this;
      }

      public UserImpl build()
      {
         return new UserImpl(address, gender, userImage, isVerified);
      }
   }

   @JsonCreator
   public UserImpl(@JsonProperty("address") Address address, @JsonProperty("gender") Gender gender, @JsonProperty("userImage") byte[] userImage,
         @JsonProperty("verified") boolean isVerified)
   {
      super();
      this.address = address;
      this.gender = gender;
      this.userImage = userImage;
      this.isVerified = isVerified;
   }

   public Address getAddress()
   {
      return address;
   }

   public Gender getGender()
   {
      return gender;
       }

   public byte[] getUserImage()
   {
      return userImage;
   }

   public boolean isVerified()
   {
      return isVerified;
   }

   @Override
   public String toString()
   {
      StringBuilder builder2 = new StringBuilder();
      builder2.append("UserImpl [address=");
      builder2.append(address);
      builder2.append(", gender=");
      builder2.append(gender);
      builder2.append(", isVerified=");
      builder2.append(isVerified);
      builder2.append(", name=");
      builder2.append(", userImage=");
      builder2.append(Arrays.toString(userImage));
      builder2.append("]");
      return builder2.toString();
   }

}

package com.art.starter.jackson_starter;

import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;

public final class AddressImpl implements Address
{
   private final String city;
   private final String country;
   private final String street;
   private final String state;
   private final int Zip;

   public static class AddressBuilder
   {
      private String city;
      private String country;
      private String street;
      private String state;
      private int Zip;

      public AddressBuilder setCity(String city)
      {
         this.city = city;
         return this;
      }

      public AddressBuilder setCountry(String country)
      {
         this.country = country;
         return this;
      }

      public AddressBuilder setStreet(String street)
      {
         this.street = street;
         return this;
      }

      public AddressBuilder setState(String state)
      {
         this.state = state;
         return this;
      }

      public AddressBuilder setZip(int Zip)
      {
         this.Zip = Zip;
         return this;
      }

      public AddressImpl build()
      {
         return new AddressImpl(city, country, street, state, Zip);
      }

   }

   @JsonCreator
   public AddressImpl(@JsonProperty("city") String city, @JsonProperty("country") String country, @JsonProperty("street") String street,
         @JsonProperty("state") String state, @JsonProperty("Zip") int Zip)
   {
      this.city = city;
      this.country = country;
      this.street = street;
      this.state = state;
      this.Zip = Zip;
   }

   public String getCity()
   {
      return city;
   }

   public String getCountry()
   {
      return country;
   }

   public String getStreet()
   {
      return street;
   }

   public String getState()
   {
      return state;
   }

   public int getZip()
   {
      return Zip;
   }

   @Override
   public String toString()
   {
      StringBuilder builder = new StringBuilder();
      builder.append("AddressImpl [city=");
      builder.append(city);
      builder.append(", country=");
      builder.append(country);
      builder.append(", state=");
      builder.append(state);
      builder.append(", street=");
      builder.append(street);
      builder.append(", Zip=");
      builder.append(Zip);
      builder.append("]");
      return builder.toString();
   }

}

問題はアドレスにあるようです。この例外が発生します:

Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.art.starter.jackson_starter.Address, problem: abstract types can only be instantiated with additional type information
 at [Source: Java.io.StringReader@785f8172; line: 1, column: 2]
    at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.Java:163)
    at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.Java:212)
    at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.Java:97)
    at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.Java:230)
    at org.codehaus.jackson.map.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.Java:595)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.Java:472)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:350)
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.Java:2391)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.Java:1614)
    at com.art.starter.jackson_starter.App.main(App.Java:56)

これは、具体的な実装であるAddressImplへのインターフェイスであるAddressをJacksonが解決する方法がないためだと確信しています。私はドキュメントを調べて、@ JsonDeserialize(as = AddressImpl.class)に関するいくつかの記事を調べましたが、機能しませんでした。だから私は困惑しています。誰かがこれを機能させたことがありますか、それはサポートされていますか?

AddressクラスでAddressImplUserImplに置き換えると、チャンピオンのように機能します。

17
nsfyn55

まだご覧になっていない方のために、不変オブジェクトとJacksonの操作について説明している ブログエントリ をご覧ください。

ただし、@JsonDeserialize(as=AddressImpl.class);をAddress.Javaインターフェイスに追加するか(直接またはミックスインを使用して)、フィールドまたはプロパティに追加することで、確実に使用できるはずです。注意すべきことの1つは、逆シリアル化の場合、使用するアクセサーの隣になければならないということです。フィールドの横にセッターがある場合はセッター。ない場合はセッター。アノテーションは(まだ)アクセサー間で共有されていません。したがって、たとえば「getter」に追加しても機能しません。

Jackson 1.8では、最終的に抽象から具象への型の登録も可能になります(詳細については、 http://jira.codehaus.org/browse/JACKSON-464 を参照)。これは、それを示すための最良のオプションである可能性があります。 「AddressImpl」は「Address」に使用されます。

20
StaxMan