2016-04-15 2 views
4

Ich möchte einen Typ an eine Funktion in Scala übergeben.Typ in Scala übergeben als Argument

Problem im Detail

ersten Iteration

Ich habe die folgenden Java-Klassen (von einer externen Quelle kommen):

public class MyComplexType { 
    public String name; 
    public int number; 
} 

und

public class MyGeneric<T> { 
    public String myName; 
    public T myValue; 
} 

In diesem Beispiel möchte ich MyComplexType den tatsächlichen Typ von MyGeneric; Im wirklichen Problem gibt es mehrere Möglichkeiten.

Ich möchte einen JSON-String mit einem Scala Code deserialisiert wie folgt:

import org.codehaus.jackson.map.ObjectMapper 

object GenericExample { 
    def main(args: Array[String]) { 
    val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
    val objectMapper = new ObjectMapper() 
    val myGeneric: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, classOf[MyGeneric[MyComplexType]]) 
    val myComplexType: MyComplexType = myGeneric.myValue 
    } 
} 

kompiliert es in Ordnung, aber Laufzeitfehler:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyComplexType 
     at GenericExample$.main(GenericExample.scala:9) 

Zweite Iteration

Arbeitslösung zu dem Problem:

val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
val objectMapper = new ObjectMapper() 
val myGeneric: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, classOf[MyGeneric[MyComplexType]]) 
myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOf[MyComplexType]) 
val myComplexType: MyComplexType = myGeneric.myValue 

Nicht schön, aber funktioniert. (Wenn jemand weiß, wie man es besser zu machen, das wäre auch willkommen.)

dritte Iteration

Die Linien in der Lösung der zweiten Iteration auftritt in dem eigentlichen Problem mehrmals, deshalb mag ich ein erstellen Funktion. Die Änderungsvariablen sind die JSON-formatierte Zeichenfolge und die MyComplexType.

Ich möchte etwas wie folgt aus:

def main(args: Array[String]) { 
    val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
    val myGeneric = extractMyGeneric[MyComplexType](jsonString) 
    val myComplexType: MyComplexType = myGeneric.myValue 
} 

private def extractMyGeneric[T](jsonString: String) = { 
    val objectMapper = new ObjectMapper() 
    val myGeneric = objectMapper.readValue(jsonString, classOf[MyGeneric[T]])  
    myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOf[T]) 
    myGeneric 
} 

Das funktioniert nicht (Compiler-Fehler). Ich habe bereits mit verschiedenen Kombinationen von Class, ClassTag, classOf gespielt, aber keiner von ihnen half. Es gab auch Compiler- und Laufzeitfehler. Weißt du, wie man einen solchen Typ in Scala übergibt? Vielen Dank!

Antwort

1

Wenn Sie jackson verwenden, um Json zu analysieren, können Sie TypeReference verwenden, um generic zu analysieren. Beispiel:

val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
val objectMapper = new ObjectMapper() 
val reference = new TypeReference[MyGeneric[MyComplexType]]() {} 
val value: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, reference) 

wenn Sie noch verwenden Jackson wollen, ich glaube, Sie einen Parameter mit TypeReference Typ erstellen können.wie:

implicit val typeReference = new TypeReference[MyGeneric[MyComplexType]] {} 
    val value = foo(jsonString) 
    println(value.myValue.name) 


    def foo[T](jsonStr: String)(implicit typeReference: TypeReference[MyGeneric[T]]): MyGeneric[T] = { 
    val objectMapper = new ObjectMapper() 
    objectMapper.readValue(jsonStr, typeReference) 
    } 
+0

Vielen Dank für die Antwort! Um es zu erweitern: Die 'TypeReference' sollte wie folgt importiert werden:' import org.codehaus.jackson. \ 'Type \' .TypeReference'. –

+0

Dies ist eine nette Lösung für "erste Iteration" Problem, danke! Es löst jedoch nicht das in der "dritten Iteration" aufgeworfene Problem, d. H. Das Hauptproblem. Das ist noch offen; In der Tat gibt es noch ein paar andere Dinge zu tun, daher wäre es nett, eine separate Funktion zu erstellen. Die einfachste Lösung (die Übergabe von 'MyGeneric [MyComplexType]' 'als generischer Typ) führt zu einem Laufzeitfehler' java.lang.ClassCastException: java.util.LinkedHashMap kann nicht in MyGeneric' umgewandelt werden. –

+0

@ CsabaFaragó Sie können mein Update überprüfen, hoffe es kann Ihnen helfen. – chengpohi

1

Ihr Ansatz, ich denke, das ist, wie Sie Klassen erhalten können, die Sie ClassTag s mit benötigen:

def extractMyGeneric[A : ClassTag](jsonString: String)(implicit generic: ClassTag[MyGeneric[A]]): MyGeneric[A] = { 
    val classOfA = implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[A]] 
    val classOfMyGenericOfA = generic.runtimeClass.asInstanceOf[Class[MyGeneric[A]]] 
    val objectMapper = new ObjectMapper() 
    val myGeneric = objectMapper.readValue(jsonString, classOfMyGenericOfA) 
    myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOfA) 
    myGeneric 
} 

Ich bin nicht vertraut mit jackson aber in ihr Spiel-json könnte leicht definieren Reads für Ihre generische Klasse wie folgt diese

import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 

implicit def genReads[A: Reads]: Reads[MyGeneric[A]] = (
    (__ \ "myName").read[String] and 
    (__ \ "myValue").read[A] 
)((name, value) => { 
    val e = new MyGeneric[A] 
    e.myName = name 
    e.myValue = value 
    e 
}) 

Mit und unter der Voraussetzung, dass die Instanz von Reads für MyComplexType existiert, y ou kann Ihre Methode als

def extractMyGeneric[A: Reads](jsonString: String): MyGeneric[A] = { 
    Json.parse(jsonString).as[MyGeneric[A]] 
} 

das Problem hier ist implementieren, dass Sie Reads für alle Ihre komplexen Typen zur Verfügung stellen müssen, die so einfach sein würde, wie

implicit complexReads: Reads[MyComplexType] = Json.reads[MyComplexType] 

wenn jene Fallklassen waren, otherways Ich denke, Sie müssten sie manuell in ähnlicher Weise definieren, wie ich es mit genReads gemacht habe.

+0

Vielen Dank für Ihre ausführliche Antwort! Währenddessen löste der andere Thread mein Problem, zusammen mit einer Verbesserung von Jackson. Ich spare Ihre Lösung für mögliche weitere typbezogene Probleme. –