2017-12-15 3 views
0

json4s (scala)를 통해 다른 내부 사례 클래스를 구문 분석하려는 많은 외부 JSON 엔터티가 있습니다. json4s의 추출 함수를 통해 모든 것이 잘 작동합니다. 나는 타입과 json 문자열을 취하고 타입/케이스 클래스에 문자열을 파싱하는 parse 함수를 구현했다. 내가 여기에 반복을 좋아하지 않아이문자열에서 클래스 형식을 스칼라로 매핑하기

entityName match { 
case "entity1" => JsonParser.parse[Entity1](jsonString) 
case "entity2" => JsonParser.parse[Entity2](jsonString) 
.... 

처럼 보이는 나는 패턴 매칭 기능을 구현 한 올바른 경우 클래스에 대한 올바른 JSON 문자열을 매핑하려면이 같은지도를 통해이 매핑을하고 싶습니다 : 내가 한 번만이 작동하지 않는이

JsonParser.parse[mapping(entityName)](jsonString) 

같은 JsonParser.parse 기능을 구현할 수있는 장소에서이지도

val mapping = Map(
"entity1" -> Entity1, 
"entity2" -> Entity2 
.... 

, 맵]을 참조이기 때문에 클래스 유형이 아니라 객체에 대한 ncing. 나 또한 classOf [Entity1] 시도했지만이 또한 작동하지 않습니다. 이것을 할 수있는 방법이 있습니까?

감사합니다.

+0

을 음 ... 당신은 컴파일 타임 물건과 런타임 물건을 혼합하고 있습니다. –

답변

0

: 같은 (match/case에 대한 필요없이) 형식을 사용하기에 충분

{ 
    import $ivy.`org.json4s::json4s-native:3.6.0-M2` 
    import org.json4s.native.JsonMethods.parse 
    import org.json4s.DefaultFormats 
    import org.json4s.JValue 

    case class Entity1(name : String, value : Int) 
    case class Entity2(name : String, value : Long) 

    implicit val formats = DefaultFormats 
    def extract[T](input : JValue)(implicit m : Manifest[T]) = input.extract[T] 

    val mapping: Map[String, Manifest[_]] = Map(
     "entity1" -> implicitly[Manifest[Entity1]], 
     "entity2" -> implicitly[Manifest[Entity2]] 
    ) 

    val input = parse(""" { "name" : "abu", "value" : 1 } """) 
    extract(input)(mapping("entity1")) //Entity1("abu", 1) 
    extract(input)(mapping("entity2")) //Entity2("abu", 1L) 
    } 
+0

감사합니다. @SergGr의 답변과 함께 도움이되었습니다. – Lothium

1

JsonParser.parse의 작동 방식은 스칼라에서 불가능합니다. 스칼라는 강력하고 정적으로 입력 된 언어입니다. 컴파일러는 유효한 필드와 메서드 만 액세스하고 메서드에 유효한 매개 변수로 전달하는지 여부를 확인할 수 있도록 컴파일 타임에 값 유형을 알아야합니다. 수업은

case class Entity1(value:Int, unique1:Int) 
case class Entity2(value:String, unique2:String) 

하고 컴파일러가 parsed의 유형 parsed.value의 유형을 알고 또는 parsed.unique2가 아닌 동안 parsed.unique1 유효한 필드 것을 알고 알 수있는 방법

val parsed = JsonParser.parse[mapping("entity1")](jsonString) 

를 쓰기 가정? 가장 좋은 유형의 컴파일러는 parsed에 매우 Any과 같은 매우 일반적인 것입니다. 물론 Any을 특정 유형으로 추후에 옮길 수는 있지만, 이것은 어떤 종류의 모든 목적을 무효로하는지 asInstanceOf에서 명시 적으로 명시해야 함을 의미합니다.

import org.json4s.jackson.JsonMethods 
implicit val formats = org.json4s.DefaultFormats // or whatever Formats you actually need 

val typeMap: Map[String, scala.reflect.Manifest[_]] = Map(
    "String" -> implicitly[scala.reflect.Manifest[String]], 
    "Int" -> implicitly[scala.reflect.Manifest[Int]] 
) 

def parseAny(typeName: String, jsonString: String): Any = { 
    val jValue = JsonMethods.parse(jsonString) 
    jValue.extract(formats, typeMap(typeName)) 
} 

을하고이 같은 수행합니다 : 아직도, 어떻게 든 Any를 반환하는 것은 당신을 위해 OK 경우, 당신은 이런 식으로 뭔가를 시도 할 수

def testParseByTypeName(typeName: String, jsonString: String): Unit = { 
    try { 
    val parsed = parseAny(typeName, jsonString) 
    println(s"parsed by name $typeName => ${parsed.getClass} - '$parsed'") 
    } catch { 
    case e => println(e) 
    } 
} 

def test() = { 
    testParseByTypeName("String", "\"abc\"") 
    testParseByTypeName("Int", "123") 
} 

P.S.을 entityName이 외부에서 제공되지 않은 경우 (즉, 실제 유형을 찾기 위해 데이터를 분석하지 않는 경우) 실제로는 필요하지 않습니다. 코드 조각이 암몬 REPL에 붙여로, @SergGr의 아이디어에 따라

def parse[T](jsonString: String)(implicit mf: scala.reflect.Manifest[T]): T = { 
    val jValue = JsonMethods.parse(jsonString) 
    jValue.extract[T] 
} 

def testParse[T](prefix: String, jsonString: String)(implicit mf: scala.reflect.Manifest[T]): Unit = { 
    try { 
    val parsed = parse[T](jsonString) 
    println(s"$prefix => ${parsed.getClass} - '$parsed'") 
    } catch { 
    case e => println(e) 
    } 
} 

def test() = { 
    testParse[String]("parse String", "\"abc\"") 
    testParse[Int]("parse Int", "123") 
}