java.lang.ClassCastException: java.util.LinkedHashMap kann nicht in com.testing.models.Account umgewandelt werden

83

Ich erhalte den folgenden Fehler:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.testing.models.Account

mit dem folgenden Code

final int expectedId = 1;

Test newTest = create();

int expectedResponseCode = Response.SC_OK;

ArrayList<Account> account = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newTest.id() + "/users")
    .as(ArrayList.class);
assertThat(account.get(0).getId()).isEqualTo(expectedId);

Gibt es einen Grund, warum ich das nicht kann get(0)?

Leidenschaftlicher Ingenieur
quelle
Kann nicht auf was gegossen werden ? Was ist der Rest der Fehlermeldung?
Oliver Charlesworth
1
@OliverCharlesworth hat auch den gesamten Stacktrace hinzugefügt
Passionate Engineer
2
Was ist ein Account? Warum versuchst du es von einer Karte aus zu besetzen?
Dave Newton
Können Sie für diejenigen von uns, die mit der Bibliothek möglicherweise nicht vertraut sind, sagen, aus welcher Klasse diese given()Methode statisch importiert wird?
Mark Peters
@ DaveNewton Accountist ein Modell von Dropwizard, das verwendetcom.fasterxml.jackson.databind.annotations
Passionate Engineer

Antworten:

107

Das Problem kommt von Jackson. Wenn es nicht genügend Informationen darüber gibt, in welche Klasse es deserialisiert werden soll, wird es verwendet LinkedHashMap.

Da Sie Jackson nicht über den Elementtyp Ihres Elements informieren ArrayList, weiß er nicht, dass Sie in ein ArrayListvon Accounts deserialisieren möchten . Es fällt also auf die Standardeinstellung zurück.

Stattdessen könnten Sie wahrscheinlich das verwenden as(JsonNode.class)und dann ObjectMapperauf eine reichhaltigere Art und Weise damit umgehen , als es die Sicherheit zulässt. Etwas wie das:

ObjectMapper mapper = new ObjectMapper();

JsonNode accounts = given().when().expect().statusCode(expectedResponseCode)
    .get("accounts/" + newClub.getOwner().getCustId() + "/clubs")
    .as(JsonNode.class);


//Jackson's use of generics here are completely unsafe, but that's another issue
List<Account> accountList = mapper.convertValue(
    accounts, 
    new TypeReference<List<Account>>(){}
);

assertThat(accountList.get(0).getId()).isEqualTo(expectedId);
Mark Peters
quelle
Sie können die Antwort auch als String () festlegen, sodass keine zusätzlichen Konvertierungen erforderlich sind. ReadValue akzeptiert einen String als erstes Argument.
BIGDeutsch
@BIGDeutsch: Nur noch einmal, ich musste dort nicht wieder in Token konvertieren, wenn convertValuedies in einem Schritt möglich war. Zu einem String zu gehen funktioniert auch.
Mark Peters
43

Versuche Folgendes:

POJO pojo = mapper.convertValue(singleObject, POJO.class);

oder:

List<POJO> pojos = mapper.convertValue(
    listOfObjects,
    new TypeReference<List<POJO>>() { });

Weitere Informationen finden Sie unter Konvertierung von LinkedHashMap .

user2578871
quelle
3
+1 für die Anzeige von "convertValue". Ich hatte einen Fall, in dem ich eine bestimmte Eigenschaft aus json als Liste lesen musste, und genau das brauchte ich.
GameSalutes
28

Die Art und Weise, wie ich das Problem mit dem JSON-Array für die Sammlung von LinkedHashMap-Objekten verringern konnte , war die Verwendung von CollectionTypeanstelle von a TypeReference. Das habe ich getan und gearbeitet :

public <T> List<T> jsonArrayToObjectList(String json, Class<T> tClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, tClass);
    List<T> ts = mapper.readValue(json, listType);
    LOGGER.debug("class name: {}", ts.get(0).getClass().getName());
    return ts;
}

Mit dem habe TypeReferenceich immer noch eine ArrayList von LinkedHashMaps erhalten, dh funktioniert nicht :

public <T> List<T> jsonArrayToObjectList(String json, Class<T> tClass) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    List<T> ts = mapper.readValue(json, new TypeReference<List<T>>(){});
    LOGGER.debug("class name: {}", ts.get(0).getClass().getName());
    return ts;
}
Himadri Pant
quelle
1
Ich habe auch mein Problem gespeichert. Vielen Dank!
Vladimir Gilevich
1
+1, der Unterschied in Ihrem Fall war, dass die Methode selbst generisch war und daher Tnicht über das TypeToken bestätigt werden konnte, was normalerweise einfacher ist. Persönlich bevorzuge ich Guavas Helfer, weil er immer noch typsicher und für keinen Containertyp spezifisch ist : return new TypeToken<List<T>>(){}.where(new TypeParameter<T>(){}, tClass). Aber Jackson nimmt TypeTokens nicht, also brauchst du es .getType().
Mark Peters
Ich habe auch mein Problem gespeichert. Vielen Dank!
Shashank
6

Ich hatte eine ähnliche Ausnahme (aber ein anderes Problem) - java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.bson.Documentund zum Glück ist es einfacher zu lösen:

Anstatt

List<Document> docs = obj.get("documents");
Document doc = docs.get(0)

was Fehler in der zweiten Zeile gibt, kann man verwenden

List<Document> docs = obj.get("documents");
Document doc = new Document(docs.get(0));
nurb
quelle
1

Lösen Sie das Problem mit zwei Methoden

  1. Mit Typ ist ein Objekt
public <T> T jsonToObject(String json, Class<T> type) {
        T target = null;
        try {
            target = objectMapper.readValue(json, type);
        } catch (Jsenter code hereonProcessingException e) {
            e.printStackTrace();
        }
    
        return target;
    }
  1. Mit Typ ist Sammlung Wrap-Objekt
public <T> T jsonToObject(String json, TypeReference<T> type) {
    T target = null;
    try {
        target = objectMapper.readValue(json, type);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    return target;
}
Liebe Java
quelle
0

Ich habe diese Methode zum Deserialisieren eines XML und Konvertieren des Typs:

public <T> Object deserialize(String xml, Class objClass ,TypeReference<T> typeReference ) throws IOException {
    XmlMapper xmlMapper = new XmlMapper();
    Object obj = xmlMapper.readValue(xml,objClass);
    return  xmlMapper.convertValue(obj,typeReference );   
}

und das ist der Aufruf:

List<POJO> pojos = (List<POJO>) MyUtilClass.deserialize(xml, ArrayList.class,new TypeReference< List< POJO >>(){ });
Mohamed Farghaly
quelle
0

Wenn Sie Jackson verwenden, um eine Zeichenfolge Ihrer konkreten Klasse zuzuordnen, insbesondere wenn Sie mit einem generischen Typ arbeiten. Dann kann dieses Problem aufgrund eines anderen Klassenladeprogramms auftreten. ich habe es einmal mit unten szenarior getroffen:

Projekt B hängt von Bibliothek A ab

in Bibliothek A:

public class DocSearchResponse<T> {
 private T data;
}

Es verfügt über einen Dienst zum Abfragen von Daten aus externen Quellen und zum Konvertieren in eine konkrete Klasse mit Jackson

public class ServiceA<T>{
  @Autowired
  private ObjectMapper mapper;
  @Autowired
  private ClientDocSearch searchClient;

  public DocSearchResponse<T> query(Criteria criteria){
      String resultInString = searchClient.search(criteria);
      return convertJson(resultInString)
  }
}

public DocSearchResponse<T> convertJson(String result){
     return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
  }
}

in Projekt B:

public class Account{
 private String name;
 //come with other attributes
}

und ich benutze ServiceA aus der Bibliothek, um Abfragen zu machen und auch Daten zu konvertieren

public class ServiceAImpl extends ServiceA<Account> {
    
}

und nutzen Sie das

public class MakingAccountService {
    @Autowired
    private ServiceA service;
    public void execute(Criteria criteria){
      
        DocSearchResponse<Account> result = service.query(criteria);
        Account acc = result.getData(); //  java.util.LinkedHashMap cannot be cast to com.testing.models.Account
    }
}

Es kommt vor, dass Jackson vom Klassenladeprogramm von LibraryA die Account-Klasse nicht laden kann und dann einfach die Methode convertJsonin Projekt B überschreibt , damit Jackson seine Arbeit erledigen kann

public class ServiceAImpl extends ServiceA<Account> {
        @Override
        public DocSearchResponse<T> convertJson(String result){
         return mapper.readValue(result, new TypeReference<DocSearchResponse<T>>() {});
      }
    }
 }
Onkel Bob
quelle