Jackson - Genel sınıfı kullanarak serisini kaldırma


Yanıtlar:


239

TypeReferenceKullandığınız her genel tip için bir nesne oluşturmanız ve bunu serileştirme için kullanmanız gerekir. Örneğin -

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

TypeReference <Veri <T>> () {} olarak kullanmak zorunda ... Ama şu hatayı alıyorum - java.lang.class özel java.lang.class.Class () erişemiyor. Erişim ayarlanamadı. Java.lang.Class yapıcısını erişilebilir
yapamıyorum

Hayır, değil Data<T>, bu bir tür DEĞİL. Gerçek sınıfı belirtmelisiniz; aksi halde aynıdır Data<Object>.
StaxMan

18
Çalışma zamanına kadar hangi sınıf olduğunu bilmiyorsam? Sınıfı çalışma zamanı sırasında parametre olarak alacağım. Bu genel gibi <T> void deSerialize (Sınıf <T> clazz {ObjectMapper mapper = new ObjectMapper (); mapper.readValue (jsonString, yeni TypeReference <Json <T>> () {});}
gnjago

1
Sorunun tamamını burada doğru bir şekilde sordum stackoverflow.com/questions/11659844/…
gnjago

tam paket adı TypeReferencenedir? değil com.fasterxml.jackson.core.typemi?
Lei Yang

88

Bunu yapamazsınız: gibi tamamen çözülmüş bir tür belirtmelisiniz Data<MyType>. Tsadece bir değişkendir ve anlamsızdır.

Ancak Tbunun bilineceğini söylüyorsanız, sadece statik olarak değil, TypeReferencedinamik olarak eşdeğer oluşturmanız gerekir . Referans verilen diğer sorular zaten bundan bahsedebilir, ancak şöyle görünmelidir:

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
   return mapper.readValue(json, type);
}

2
Çalışma zamanına kadar hangi sınıf olduğunu bilmiyorsam? Sınıfı çalışma zamanı sırasında parametre olarak alacağım. Bu genel gibi <T> void deSerialize (Sınıf <T> clazz {ObjectMapper mapper = new ObjectMapper (); mapper.readValue (jsonString, yeni TypeReference <Json <T>> () {});}
gnjago

1

2
O zaman sınıfı olduğu gibi iletin, gerek yok TypeReference: return mapper.readValue(json, clazz);Burada sorun tam olarak nedir?
StaxMan

2
Sorun "Veri" genel bir sınıf olmasıdır. Çalışma zamanında hangi T tipini belirtmem gerekiyor. Clazz parametresi çalışma zamanında T bize ne olduğunu. Peki, readValue nasıl çağırılır? yeni TypeReference ile arama> Json <T>> çalışmıyor Tüm soru burada stackoverflow.com/questions/11659844/…
gnjago

2
Tamam. O zaman kullanmalısın TypeFactory.. Cevabımı düzenleyeceğim.
StaxMan

30

Yaptığınız ilk şey serileştirmek, sonra serileştirmeyi yapabilirsiniz.
serileştirdiğinizde, @JsonTypeInfojackson'un sınıf bilgilerini json verilerinize yazmasına izin vermek için kullanmalısınız . Yapabilecekleriniz şöyle:

Class Data <T> {
    int found;
    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    Class<T> hits
}

Sonra serileştirmeyi kaldırdığınızda, jackson'unuzun değişken isabetlerinizin gerçekte çalışma zamanında olduğu bir sınıfa serileştirilmiş olduğunu göreceksiniz.


çalışmıyor, com.fasterxml.jackson.databind.exc.InvalidTypeIdException hatasını almama: [basit tip, sınıf java.lang.Object] alt türünü çözmeye çalışırken eksik tip kimliği yok (POJO için) property 'data')
gaurav9620

15

Sınıf Verileri için <>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)

Bu artık kullanımdan kaldırıldı.
Evan Gertis

13

Sadece Util sınıfında statik bir yöntem yazın. Bir dosyadan bir Json okuyorum. String'i readValue öğesine de verebilirsiniz

public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}

Kullanımı:

List<TaskBean> list =  Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);

7

Genel türünüzün türünü bilen başka bir sınıfa sarabilirsiniz.

Örneğin,

class Wrapper {
 private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);

Burada bir şey somut bir tiptir. Reified tip başına bir sargıya ihtiyacınız var. Aksi takdirde Jackson hangi nesneleri yaratacağını bilmiyor.


6

Deserialize edilmesi gereken JSON dizesi, parametre hakkında tür bilgisini içermelidir T. Parametre türü hakkında tür bilgilerinin Jackson tarafından JSON dizesine okunabilmesi / yazılabilmesi için , sınıfa
parametre olarak iletilebilen her sınıfa Jackson ek açıklamaları koymanız gerekir .TDataT

TBunun soyut sınıfı genişleten herhangi bir sınıf olabileceğini varsayalım Result.

class Data <T extends Result> {
    int found;
    Class<T> hits
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
        @JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {

}

public class ImageResult extends Result {

}

public class NewsResult extends Result {

}

Parametre olarak iletilebilen sınıfın her biri (ya da ortak süpertipleri) T açıklama eklendikten sonra Jackson, JSON'da parametre hakkında bilgi içerir T. Böyle bir JSON daha sonra Tderleme zamanında parametreyi bilmeden serisini kaldırabilir .
Bu Jackson dokümantasyon bağlantısı Polimorfik Deserializasyon hakkında konuşuyor ancak bu soruya da değinmek faydalı.


ve bir Listeye sahip olmak istersem bunu nasıl yönetebilirim? Diyelim ki Liste <ImageResult>
Luca Archidiacono

5

Jackson 2.5'ten, parametreleştirilmiş sınıfı ve parametreli türlerini belirleyerek bir Jackson'ı düz olarak tanımlamaya izin veren TypeFactory.constructParametricType (Class parametreli, Class ... parametreClasses) yöntemini kullanarak çözmenin zarif bir yolu JavaType.

Sürümü kaldırmak istediğinizi varsayarsak Data<String>, şunları yapabilirsiniz:

// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);

Sınıf birden çok parametreli tür bildirirse, bunun daha zor olmayacağını unutmayın:

class Data <T, U> {
    int found;
    Class<T> hits;
    List<U> list;
}

Yapabilirdik :

JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);

Harika, teşekkürler benim için çalıştı. Typereference ile haritadan belirli bir nesneye classcast istisnası aldım ama bu gerçekten işi yapıyor.
Tacsiazuma

1
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
    private Class<T> cls;
    public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
        cls = (Class<T>) ctx.getContextualType().getRawClass();
        return this;
    }
    ...
 }

0

scala kullanıyorsanız ve derleme zamanında genel türü biliyorsanız, ancak tüm uygulamalarınızda TypeReference'ı manuel olarak geçirmek istemiyorsanız, aşağıdaki kodu kullanabilirsiniz (jackson 2.9.5 ile):

def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {

    //nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
    // new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function

    def recursiveFindGenericClasses(t: Type): JavaType = {
      val current = typeTag.mirror.runtimeClass(t)

      if (t.typeArgs.isEmpty) {
        val noSubtypes = Seq.empty[Class[_]]
        factory.constructParametricType(current, noSubtypes:_*)
      }

      else {
        val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
        factory.constructParametricType(current, genericSubtypes:_*)
      }

    }

    val javaType = recursiveFindGenericClasses(typeTag.tpe)

    json.readValue[T](entityStream, javaType)
  }

Bu şekilde kullanılabilir:

read[List[Map[Int, SomethingToSerialize]]](inputStream)

0

Genel JSON dizesini Jackson ile Java nesnesine serileştirmek için şunlara ihtiyacınız vardır:

  1. Bir JSON sınıfı tanımlamak için.

  2. Bir öznitelik eşlemesi gerçekleştirin.

Son kod, test edilmiş ve kullanıma hazır:

static class MyJSON {

    private Map<String, Object> content = new HashMap<>();

    @JsonAnySetter
    public void setContent(String key, Object value) {
        content.put(key, value);
    }
}

String json = "{\"City\":\"Prague\"}";

try {

    MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);

    String jsonAttVal = myPOJO.content.get("City").toString();

    System.out.println(jsonAttVal);

} catch (IOException e) {
    e.printStackTrace();
}

Önemli:
@JsonAnySetter ek açıklama zorunludur, genel bir JSON ayrıştırma ve popülasyon sağlar.

İç içe dizilerle daha karmaşık durumlar için lütfen Baeldung referansına bakın: https://www.baeldung.com/jackson-mapping-dynamic-object


0

Çok iyi değil, basit bir karar örneği (sadece Jackson için değil, Spring RestTemplate vb. İçin):

Set<MyClass> res = new HashSet<>();
objectMapper.readValue(json, res.getClass());
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.