1

편집은 : 그것은 내 문제를 좁히는 데 도움이 필요한 정보를 포함하지 않은으로컴파일러 실패

마지막 개정은 도움이되지 않는 것으로 간주되었다. 그러므로 AST도 포함시킬 필요가 있습니다.

다음은 사용자 정의 스키마를 기반으로 play-json의 json을 구문 분석하고 쓰는 것을 허용하는 라이브러리 전체입니다.

import scala.language.higherKinds 
import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 

import scala.language.{higherKinds, implicitConversions} 

type PathNodes = List[PathNode] 

sealed trait Field[A] { 

    def pathNodes: PathNodes 

    def jsPath: JsPath = JsPath(pathNodes) 

    def relativePath: JsPath = JsPath(List(pathNodes.last)) 

    def format: Format[A] 

    def nestedFormatter(path: JsPath): OFormat[A] 

    def nestedFormat: OFormat[A] = nestedFormatter(relativePath) 
} 

case class PlainField[A: Format](prefix: PathNodes) extends Field[A] { 

    override def pathNodes: PathNodes = prefix 

    def format: Format[A] = implicitly[Format[A]] 

    override def nestedFormatter(path: JsPath): OFormat[A] = path.format(format) 
} 

abstract class JsonSchema[T](val _prefix: PathNodes) extends Field[T] with SchemaExtensionMethods { 

    override def pathNodes: PathNodes = _prefix 

    def format: OFormat[T] 

    protected def plain[A: Format](name: String): PlainField[A] = PlainField[A](_prefix :+ KeyPathNode(name)) 

    protected def nested[N](name: String, factory: PathNodes => N): N = factory(_prefix :+ KeyPathNode(name)) 

    protected def nested[B, G <: JsonSchema[B]](name: String)(implicit sm: HasJsonSchema[B, G]): G = sm.apply(_prefix :+ KeyPathNode(name)) 

    override def nestedFormatter(path: JsPath): OFormat[T] = path.format(format) 
} 

case class Optional[F, A](field: F)(implicit ev: F <:< Field[A]) extends Field[Option[A]] { 

    override def pathNodes: PathNodes = field.pathNodes 

    override def format: Format[Option[A]] = { 
    implicit val writes: Writes[Option[A]] = JsPath.writeNullable(field.format) 
    implicit val reads: Reads[Option[A]] = JsPath.readNullable(field.format) 
    implicitly[Format[Option[A]]] 
    } 

    def map[G, B](f: F => G)(implicit ev: G <:< Field[B]): Optional[G, B] = new Optional[G, B](f(field)) 

    def flatMap[G <: Field[B], B](f: F => Optional[G, B]): Optional[G, B] = f(field) 

    override def nestedFormatter(path: JsPath): OFormat[Option[A]] = path.formatNullable(field.format) 
} 

case class Collection[F, A](field: F)(implicit ev: F <:< Field[A], repath: Repath[F]) extends Field[Seq[A]] { 
    override def pathNodes: PathNodes = field.pathNodes 

    override def format: Format[Seq[A]] = { 
    implicit val writes: Writes[Seq[A]] = Writes.seq(field.format) 
    implicit val reads: Reads[Seq[A]] = Reads.seq(field.format) 
    implicitly[Format[Seq[A]]] 
    } 

    def apply(idx: Int): F = implicitly[Repath[F]].apply(field, IdxPathNode(idx)) 

    override def nestedFormatter(path: JsPath): OFormat[Seq[A]] = path.format(format) 
} 

class FormatExtensionMethods[T](val arg: T) { 
    def <>[A, B, Fun](apply: Fun, unapply: B => Option[A])(implicit jss: JsonShape[A, B, T, Fun]): OFormat[B] = jss.format(arg, apply, unapply andThen (_.get)) 
} 

class FieldExtensionMethods[F](val field: F) { 
    def optional[A](implicit ev: F <:< Field[A]): Optional[F, A] = new Optional[F, A](field) 

    def sequence[A](implicit ev: F <:< Field[A], repath: Repath[F]): Collection[F, A] = new Collection[F, A](field) 
} 

trait SchemaExtensionMethods { 
    implicit def formatExtensionMethods[M](t: M): FormatExtensionMethods[M] = new FormatExtensionMethods[M](t) 

    implicit def fieldExtensionMethods[M, A](t: M): FieldExtensionMethods[M] = new FieldExtensionMethods[M](t) 
} 

trait Repath[F] { 
    def apply(f: F, node: PathNode): F 
} 

object Repath { 
    implicit def plain[T]: Repath[PlainField[T]] = new Repath[PlainField[T]] { 
    override def apply(t: PlainField[T], node: PathNode): PlainField[T] = 
     PlainField[T](t.pathNodes :+ node)(t.format) 
    } 

    implicit def schema[S <: JsonSchema[_]](implicit sm: HasJsonSchema[_, S]): Repath[S] = new Repath[S] { 
    override def apply(t: S, node: PathNode): S = 
     sm.apply(t.pathNodes :+ node) 
    } 

    implicit def option[F <: Field[T] : Repath, T]: Repath[Optional[F, T]] = new Repath[Optional[F, T]] { 
    override def apply(t: Optional[F, T], node: PathNode): Optional[F, T] = 
     new Optional[F, T](implicitly[Repath[F]].apply(t.field, node)) 
    } 

    implicit def sequence[F <: Field[T] : Repath, T]: Repath[Collection[F, T]] = new Repath[Collection[F, T]] { 
    override def apply(t: Collection[F, T], node: PathNode): Collection[F, T] = 
     new Collection[F, T](implicitly[Repath[F]].apply(t.field, node)) 
    } 
} 

trait JsonShape[A, B, -T, Func] { 
    def format(t: T, apply: Func, unapply: B => A): OFormat[B] 
} 

object JsonShape { 
    type F[T] = Field[T] 

    implicit def cc1[A, B]: JsonShape[A, B, F[A], (A) => B] = (t: F[A], apply: (A) => B, unapply: B => A) => { 
    val name = t.pathNodes.last.asInstanceOf[KeyPathNode].key 
    OFormat[B](
     Reads[B](jsv => (jsv \ name).validate[A](t.format).map(apply)), 
     OWrites[B](b => JsObject(Map(name -> Json.toJson(unapply(b))(t.format)))) 
    ) 
    } 

    implicit def cc2[T1, T2, B]: JsonShape[(T1, T2), B, (F[T1], F[T2]), (T1, T2) => B] = (t: (F[T1], F[T2]), apply: (T1, T2) => B, unapply: B => (T1, T2)) => { 
    (
     t._1.nestedFormat and 
     t._2.nestedFormat 
    ) (apply, unapply) 
    } 

    implicit def cc3[T1, T2, T3, B]: JsonShape[(T1, T2, T3), B, (F[T1], F[T2], F[T3]), (T1, T2, T3) => B] = (t: (F[T1], F[T2], F[T3]), apply: (T1, T2, T3) => B, unapply: B => (T1, T2, T3)) => { 
    (
     t._1.nestedFormat and 
     t._2.nestedFormat and 
     t._3.nestedFormat 
    ) (apply, unapply) 
    } 

    //this goes up to 22 

} 

abstract class HasJsonSchema[T, +S <: JsonSchema[T]](val apply: PathNodes => S) extends OFormat[T] { 
    val root: S = apply(Nil) 

    def format: OFormat[T] = root.format 

    def writes(o: T): JsObject = root.format.writes(o) 

    def reads(json: JsValue): JsResult[T] = root.format.reads(json) 
} 
이제

의 문제 재현 클라이언트 코드의 작은 조각 작성할 수 : 어떤 스칼라의 slick 마찬가지로 어느 정도 데이터베이스 열에 대해 제공 내가 큰 의심을 가지고

case class MessageSchema(prefix: PathNodes) extends JsonSchema[Message](prefix) { 

    def underlying = plain[String]("underlying") 
    //def underlying = plain[String]("underlying").optional if I wanted the field to be Option[String] 
    //def underlying = plain[String]("underlying").sequence if I wanted the field to be Seq[String] 
    override def format = underlying <> (Message.apply _, Message.unapply) 

} 

case class Message(underlying: String) 

object Message { 

    implicit object sm extends HasJsonSchema[Message, MessageSchema](MessageSchema.apply) 

} 

case class LanguageTaggedSchema[T, S <: JsonSchema[T]](prefix: PathNodes)(implicit evT: HasJsonSchema[T, S]) extends JsonSchema[LanguageTagged[T]](prefix) { 
    def lang = plain[String]("lang") 

    def data: S = nested("data")(evT) 

    def format = (lang, data) <> (LanguageTagged.apply[T] _, LanguageTagged.unapply[T]) 
} 

case class LanguageTagged[T](lang: String, data: T) 

object LanguageTagged { 

    implicit def schemaMapper[T, S <: JsonSchema[T]](implicit ev: HasJsonSchema[T, S]): HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]] = 
    new HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]](LanguageTaggedSchema.apply[T, S]) {} 
} 

def toJson[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): JsValue = Json.toJson(a)(ev.format) 

toJson(Message("hi")) //Ok! 
toJson(LanguageTagged("en", Message("hi"))) //Ok! 
//or simply write 
Json.toJson(LanguageTagged("en", Message("hi"))) 

//and if i wanted to traverse a json path i would do: 
val schema = implicitly[HasJsonSchema[LanguageTagged[Message],LanguageTaggedSchema[Message,MessageSchema]]].root 
schema.data.underlying.jsPath 
//prints: res2: play.api.libs.json.JsPath = /data/underlying 

//Now to where the problem starts: 

def getSchema[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): S = ev.root 

getSchema(Message("hi")) //Ok! 
getSchema(LanguageTagged("en", Message("hi"))) //Not Ok but why? 
//Error:(211, 11) could not find implicit value for 
//parameter ev: A$A6.this.HasJsonSchema[A$A6.this.LanguageTagged[A$A6.this.Message],S] 
//getSchema(LanguageTagged("en", Message("hi")));// 
//^ 

을 그 컴파일러 실행 (implicit type) S를 추론 할 때 S의 한정된 유형()으로 인해 모든 코드의 마지막 줄에 표시된 것처럼 특정 상황에서만 문제가됩니다. dubugging 시도로 비슷한 상황을 만들었고 S 타입이 묶여 있지 않으면이 문제가 발생하지 않는다는 것을 깨달았습니다. 코드를 리팩터링하여 바운드 형식에 의존하지 않는 코드 나 암시 적 해상도를 해결하는 코드를 사용하면 어떤 종류의 솔루션이라도 이해할 수 있습니다.

+0

하고 왜'대신'JsonSchema [의 S'를 반환 할 수있다 : 당신은, 대신에보다 깊이있는 설명 형 클래스를 사용한다 T]'? – SergGr

+0

@SergGr 필요한 정보가있는 게시물을 업데이트했습니다. – shayan

+0

죄송합니다. 일부 문제를 보여줄 수있는 코드가 있지만 실제 문제가 무엇인지 설명하지 마십시오. 이것은 심각하게 대답을 제한하고 또한 질문을 훨씬 더 어렵게 만듭니다 (대답을하기 위해서는 코드를 읽어야하기 때문입니다). 또한'root' 나'reaplies' 나'postedBy'와 같은 것들은 이전에 언급되지 않았기 때문에 여러분의 예제는 절대적으로 관련이없는 것처럼 보이기 때문에 그들이 실제로 무엇인지 추측하기가 어렵습니다. 그럼 다시 한번, 코드가 아닌 논리의 관점에서 해결하려는 문제를 설명해 주시겠습니까? – SergGr

답변