2017-09-26 9 views
0

この問題は、複数のクラスを返すことができる任意のAPIで発生しますが、タイプはAnyです。asInstanceOfボイラープレートなしのAny型の混合値の処理

具体例では、組み込みのJSONパーサー(scala.util.parsing.json)を使用してJSONを処理しています。返される値はMap[String,Any]です。これは、各JSONキーと値のペアの値が任意のJSON型であるためです。

これらのネストされたMapから値を抽出するには、タイプテストとキャスティングが必要です。これはかなり醜いです。特に、戻り値の型(String、Double、Map ...など)とは別の複数の関数があり、チェックとキャストに使用されます。

一般的なget[T](...): T関数が1つだけ必要なので、この定型文を避けてこの型を抽象化することはできますか?

私はTypeTagを見てきましたが、これまでのところ、戻り値の型ではなく引数の型を抽象化してみました。明確にするために

返すレガシーインタフェースに対応するため、私はパターンなどのマッチングと非常に良くインタフェースを提供する他の多くのJSONパーサーがあることを認識してんだけど、私はリファクタリングのこの一般的な問題でちょうど興味Anyのコレクション。

import scala.util.parsing.json._ 

object ParseJSON { 

    val text = """{"str":"A String", "num":123, "obj": { "inner":"value" }}""" 

    val json = JSON.parseFull(text).get.asInstanceOf[Map[String,Any]] 
     //> ... Map(str -> A String, num -> 123.0, obj -> Map(inner -> value)) 

    // Three essentially identical functions: 

    def getString(m:Map[String,Any], k:String): Option[String] = { 
    m.get(k).flatMap{ v => 
     if (v.isInstanceOf[String]) Some(v.asInstanceOf[String]) else None 
    } 
    }            

    def getDouble(m:Map[String,Any], k:String): Option[Double] = { 
    m.get(k).flatMap{ v => 
     if (v.isInstanceOf[Double]) Some(v.asInstanceOf[Double]) else None 
    } 
    }            

    def getObject(m:Map[String,Any], k:String): Option[Map[String, Any]] = { 
    m.get(k).flatMap{ v => 
     if (v.isInstanceOf[Map[_,_]]) Some(v.asInstanceOf[Map[String,Any]]) 
     else None 
    } 
    }            

    getString(json, "str")    //> res0: Option[String] = Some(A String) 
    getString(json, "num")    //> res1: Option[String] = None 
    getObject(json, "obj") 
        //> res3: Option[Map[String,Any]] = Some(Map(inner -> value)) 
} 

私は当初、これは一般的なクラスを経由して解決することができると思っていた:

class Getter[T] { 
    def get(m: Map[String, Any], k: String): Option[T] = { 
     m.get(k).flatMap { v => 
     if (v.isInstanceOf[T]) Some(v.asInstanceOf[T]) else None 
     } 
    } 
    } 

    new Getter[String].get(json, "str") 

が、オレグPyzhcovは(私の今-削除の回答で)指摘したように、型消去がタイプかどうかを検出するから、これを防止実行時に正しいです。

+1

パッケージ 'scala.util.parsing.json'はもはや推奨されません。むしろJSON専用ライブラリを使用する – cchantep

+0

合意 - しかし、私が質問したように、私はこのリファクタリング問題を解決する方法に興味があります(動機付けの例としてJSON解析を使用) – DNA

+0

落胆したlibの修正制限は非常に小さい – cchantep

答えて

1

修正は非常に簡単です:ClassTag[T]が利用可能な場合: Tに対する

import scala.reflect.ClassTag 

class Getter[T: ClassTag] { 
    def get(m: Map[String, Any], k: String): Option[T] = { 
    m.get(k).flatMap { 
     case v: T => Some(v) 
     case _ => None 
    } 
    } 
} 

new Getter[String].get(json, "str") 

パターンマッチングは、特別に処理されます。残念ながら

、あなたはT自体がジェネリック型になりたい場合は、型消去の逆襲:それはList、ないその種類のパラメータを渡された場合Getter[List[String]]にのみ確認することができます。

0

おそらく、私は個人的にCirceを提案する目的でJSONライブラリを使用したいと思うでしょう。それは、タイプセーフなシリアライゼーションとデシリアライズをスムーズに処理し、簡単に拡張できます。多態的な直列化を探している場合は、型をJSON内に格納する必要があり、Circeは通常、ケースクラスの密接な階層のサポートを介してこれらも処理します。あなたの失敗した試みに

関連する問題