java.lang.Classcastexception-Fehler: com.google.gson.intern.LinkedTreeMap nicht cast auf java.util.LinkedHashMap

Ich entschuldige mich für die Eröffnung eine andere Frage zu diesem Allgemeinen Problem, aber keine der Fragen die ich gefunden habe, SO scheint in engem Bezug zu mein Problem.

Ich habe eine bestehende, funktionierende Datenfluss pipeline akzeptiert, dass Objekte von KV<Long, Iterable<TableRow>> – und Ausgänge TableRow Objekte. Dieser code ist in unserer Produktionsumgebung ausgeführt wird, ohne Frage. Ich bin jetzt zu versuchen, einen unit-test mit direktem Läufer, um dies zu testen pipeline, aber die unit-test schlägt fehl, wenn es Sie trifft die Linie

LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);

in der pipeline, wirft die Fehlermeldung:

java.lang.Classcastexception-Fehler: com.google.gson.intern.LinkedTreeMap
kann nicht gewirkt werden, um java.util.LinkedHashMap

Ist eine vereinfachte version des bestehenden Datenfluss-code sieht wie folgt aus:

public static class Process extends DoFn<KV<Long, Iterable<TableRow>>, TableRow> {

    /* private variables */

    /* constructor */

    /* private functions */

    @ProcessElement
    public void processElement(ProcessContext c) throws InterruptedException, ParseException {       

      EventProcessor eventProc = new EventProcessor();
      Processor.WorkItem workItem = new Processor.WorkItem();
      Iterator<TableRow> it = c.element().getValue().iterator();

      //process all TableRows having the same id
      while (it.hasNext()) {
        TableRow item = it.next();

        if (item.containsKey(Schema.EVT))
          eventProc.process(item, workItem);
        else
          /* process by different Proc class */
      }

      /* do additional logic */

      /* c.output() is somewhere far below */

    }
}

public class EventProcessor extends Processor {

    //Extract data from an event into the WorkItem
    @SuppressWarnings("unchecked")
    @Override
    public void process(TableRow row, WorkItem item) {    

        try {
          LinkedHashMap<String, Object> evt = (LinkedHashMap<String, Object>) row.get(Schema.EVT);
          LinkedHashMap<String, Object> profile = (LinkedHashMap<String, Object>) row.get(Schema.PROFILE);

          /* if no exception, process further business logic */

          /* business logic */

        } catch (ParseException e) {
            System.err.println("Bad row");
        }
    }
}      

Den relevanten Teil des unit-test, der bereitet den Haupt-input für die Process() DoFn sieht wie folgt aus:

Map<Long, List<TableRow>> groups = new HashMap<Long, List<TableRow>>();
List<KV<Long, Iterable<TableRow>>> collections = new ArrayList<KV<Long,Iterable<TableRow>>>();    
Gson gson = new Gson();    

//populate the map with events grouped by id
for(int i = 0; i < EVENTS.length; i++) {
  TableRow row = gson.fromJson(EVENTS[i], TableRow.class);
  Long id = EVENT_IDS[i];

  if(groups.containsKey(id))
    groups.get(id).add(row);
  else
    groups.put(id, new ArrayList<TableRow>(Arrays.asList(row)));        
}

//prepare main input for pipeline
for(Long key : groups.keySet())
  collections.add(KV.of(key, groups.get(key)));

Die Zeile, die das Problem verursacht ist gson.fromJson(EVENTS[i], TableRow.class);, die zu sein scheint die Kodierung der internen Darstellung der Tabellenzeilen als die falsche Art von LinkedTreeMap.

Der kodierte Typ der Tabellenzeilen angezeigt werden com.google.gson.internal.LinkedTreeMap statt der erwarteten java.util.LinkedHashMap. Gibt es eine Möglichkeit, die ich werfen kann, ist die Tabellenzeilen erstellt wird in meinem unit-test, um die richtige Art von java.util.LinkedHashMap, so dass der unit-test ist erfolgreich, ohne dass änderungen an den vorhandenen Datenfluss-code, der funktioniert bereits in der Produktion?

  • Bitte lassen Sie mich wissen, wenn mehr code benötigt wird, zum hinzufügen von Kontext.
  • Warum müssen Sie zu werfen, um LinkedHashMap – und nicht nur Map?
  • Um ehrlich zu sein, ich bin mir nicht sicher. Der cast zu LinkedHashMap wurde der vorhandene code von irgendwann vor. Würde die änderung der cast zu Map irgendwelche Konsequenzen?
  • Genau. Code für die Schnittstelle, und ersetzen Sie LinkedHashMap mit Map statt. Dann brauchen Sie nicht zu kümmern, die map-Implementierung der API geschieht, um zurückzukehren.
  • Wenn LinkedTreeMap ist ein Kind Map es sollte kein problem sein, es sei denn, Sie verwenden etwas bestimmtes von dieser Klasse. aber ich denke, Ihre beste Wette ist, einfach mal versuchen und sehen, ob es funktioniert
  • github.com/google/gson/blob/master/gson/src/main/java/com/… Es ist eine AbstractMap, so ist es eine Map. Wenn der folgende code nicht, etwas bestimmtes zu LinkedHashMap und HashMap, sollte es ok sein. Aber natürlich würde ich zuerst testen.
  • Das scheint geklappt zu haben! Der unit-test ist zumindest nicht zu Versagen nicht mehr, aber ich werde führen Sie die Haupt-code durch Datenfluss wieder. Vielen Dank für die Hilfe, Roman!
  • Ich würde den cast, um neue wie new LinkedHashMap<String, Object>(Zeile.erhalten(Schema.EVT)); auf Diese Weise wird der code weiter arbeiten unabhängig von der return-Typ aus der Reihe.get(…) und man könnte immer die Art der LinkedHashMap in Ihrem code, die Signale für den Leser, der Typ ist eine Karte, aber auch eine, die sorgt für Ordnung. Vielleicht ist es bedauerlich, dass es nicht eine Schnittstelle für die Semantik ‚hey, das ist eine Karte, aber zur gleichen Zeit eine, die hält die Daten in der insertion order‘.
  • Toll, ich bin froh, dass es geholfen 🙂 ich habe umgebucht auf die Lösung als eine Antwort, so könnte man es akzeptieren 😉

InformationsquelleAutor Max | 2017-12-29



3 Replies
  1. 2

    Umbuchung die Lösung als Antwort.

    Ist es nicht empfehlenswert, zu werfen, um konkrete Klassen, wenn Sie nicht mit Ihren spezifischen Eigenschaften. In diesem Fall ist es besser, es zu werfen, um Map statt LinkedHashMap. Gson ist LinkedTreeMap ist ein Map auch, also kein problem entstehen sollte.

  2. 0

    Weil LinkedHashMap nicht überlegen ist LinkedTreeMap so konnten Sie nicht die gleichen Methoden. Der Java-compiler so denkt, dass du ihn so möglicherweise in evt mit unterschiedlichen Methoden als row.get(Schema.EVT), was zu schlechten Sachen.
    Sie können jedoch die Besetzung LinkedTreeMap in AbstractMap, Map oder ein Objekt, wie Sie sind allen überlegen ist.
    (Wie viele Kommentare zeigen), um es zu beheben, nutzen Sie einfach

    Map<String, Object> evt = row.get(Schema.EVT);

    und Sie sollten in Ordnung sein.

  3. 0

    Halte ich (nicht nur, dass insbesondere warf einen code smell. Jedes mal, wenn ein cast ist codiert, das Risiko genommen wird, dass ein ClassCastException passiert.

    Wie die anderen schon sagten, die Map – Schnittstelle konnte verwendet werden, wie Map<String, Object> evt = row.get(Schema.EVT);.

    Alternativ eine neue LinkedHashMap konstruiert werden könnte, durch new LinkedHashMap<String, Object>(row.get(Schema.EVT));.

    Der zweite Ansatz hat den Vorteil, dass die LinkedHashMap Art, die möglicherweise oder möglicherweise nicht wichtig sein, das hängt von Ihrem Szenario ab.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.