2015-04-24 1 views
0

Je suis actuellement aux prises avec spray-json en écrivant un protocole pour mon modèle de données. Pour la désérialisation des données JSON à mes objets de transfert de données, un DAO doit être contacté pour vérifier si un objet approprié existe, sinon un DeserializationException doit être lancé.Spray JSON: Comment obtenir des objets déclarés implicitement dans des méthodes de lecture et d'écriture?

Jusqu'à présent, je donne les résultats suivants:

object MyJsonProtocol extends DefaultJsonProtocol { 

    implicit object MyDtoJsonFormat extends RootJsonFormat[MyDto] { 

    override def write(obj: MyDto): JsValue = // Serialization implementation 

    override def read(json: JsValue): MyDto = { 
     // parse the JSON, get some parameters, let them be a, b, c 
     dtoLookup(a, b, c) match { 
     case Some(dto: MyDto) => dto 
     case None => throw new DeserializationException("Cannot retrieve object from DAO") 
     } 
    } 
    } 

    def dtoLookup(a: SomeType, b: SomeOtherType, c: YetAnotherType)(implicit dao: MyDAO): Option[MyDto] = { 
    // lookup the MyDTO with the dao instance 
    } 
} 

Mon test se présente comme suit:

class MyJsonProtocolTest extends FlatSpec with Matchers { 

    implicit val MyDAO = // some test instance, can be a mock object 

    "The protocol" should "serialize a DTO" in { 
    val dto: MyDTO = ... 
    dto.toJson.compactPrint should be("{...}") 
    } 
} 

Cependant, le compilateur se plaint qu'il ne peut pas trouver le MyDAO implicite lorsque vous essayez de compiler les MyJSONProtocol. En When testing Spray services with Scalatest, how to introduce implicit values? j'ai demandé hier, on m'a suggéré de passer le paramètre implicite directement dans la méthode, mais je ne peux pas le faire ici parce que la méthode read est définie dans le RootJsonFormat.

Lorsque j'appelle la méthode dtoLookup directement à partir de mon code de test, cela réussit. Alors, comment puis-je obtenir l'instance MyDAO dans mon format JSON spécial?

Répondre

0

Une option consiste à faire du paramètre implicite un paramètre constructeur à l'une des classes utilisées. Cela peut nécessiter de transformer l'un de vos objects en class. Vous pouvez ensuite créer une méthode get sur l'objet compagnon de cette classe qui utilise une portée implicite pour construire la classe avec l'argument souhaité.

Cela ne concerne pas vraiment spray ou scalatest, mais plutôt un problème avec les implicits et la portée implicite. Voici une version simplifiée:

object MyJsonProtocol { 
    implicit object MyDtoJsonFormat { 
    def read(x: Int) = dtoLookup 
    } 
    def dtoLookup(implicit x: Int) = x + 1 
} 

Et vous pourriez envisager de changer cela à:

class MyJsonProtocol(implicit x: Int) { 
    implicit object MyDtoJsonFormat { 
    def read(x: Int) = dtoLookup 
    } 
    def dtoLookup = x + 1 
} 

object MyJsonProtol { 
    def get(implicit x: Int) = new MyJsonProtocol 
} 

Et vous pouvez l'utiliser avec une implicite portée:

class MyJsonProtocolTest { 
    implicit val x = 5 
    val proto = MyJsonProtol.get 
    val myReadValue = proto.MyDtoJsonFormat.read //6 
} 

Vous pouvez lire les règles pour les portées implicites here, particulièrement pertinentes pourraient être la section «D'où viennent les implicites».

+0

Merci, je vais essayer. Mais pour le problème que je voulais résoudre, j'ai trouvé une autre solution que je considère comme "plus propre". J'ai créé un "Bridge" 'MyDTOBridge' qui est une classe de cas et contient tous les paramètres que je veux (dé) sérialiser, et l'utilise dans mon protocole JSON. Puis j'ai créé un 'MyDTOConverter' qui a des méthodes implicites convertissant le' MyDTO' à son pont et retour, où j'utilise le DAO comme un paramètre implicite. Je peux alors obtenir le 'MyDTO' dans un' Option' où je peux obtenir 'None' quand le DTO n'existe pas dans le DAO. – rabejens

+0

Une autre question: Comment dois-je comprendre la méthode 'get'? Est-ce appelé implicitement quand je fais le nouveau MyJSONProtocol? Est-ce similaire à «appliquer»? – rabejens