programing

재생 2 JSON 형식에서 누락된 속성에 대한 기본값

lastmemo 2023. 2. 25. 19:43
반응형

재생 2 JSON 형식에서 누락된 속성에 대한 기본값

플레이 스칼라에는 다음 모델과 동등한 모델이 있습니다.

case class Foo(id:Int,value:String)
object Foo{
  import play.api.libs.json.Json
  implicit val fooFormats = Json.format[Foo]
}

다음 Foo 인스턴스의 경우

Foo(1, "foo")

다음 JSON 문서를 받을 수 있습니다.

{"id":1, "value": "foo"}

이 JSON은 유지되고 데이터스토어에서 읽힙니다.이제 요구 사항이 변경되어 Foo에 속성을 추가해야 합니다.속성 기본값은 다음과 같습니다.

case class Foo(id:String,value:String, status:String="pending")

JSON에 쓰는 것은 문제가 되지 않습니다.

{"id":1, "value": "foo", "status":"pending"}

그러나 여기서 읽으면 "/status" 경로가 누락되어 JsError가 발생합니다.

노이즈가 최소인 디폴트를 설정하려면 어떻게 해야 합니까?

(ps: 아래에 올릴 답변이 있습니다만, 그다지 만족하지 않고, 보다 나은 선택지를 상향 투표해 받아들이겠습니다.

2.6 이상 재생

@CanardMousant의 답변에 따르면 Play 2.6부터 Play-json 매크로가 개선되었으며, 역직렬화 시 디폴트 값을 플레이스 홀더로 사용하는 것을 포함한 여러 새로운 기능을 제안합니다.

implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]

2.6 미만의 재생에서는 다음 옵션 중 하나를 사용하는 것이 좋습니다.

play-json-disply의

질문의 내용을 포함하여 플레이제이슨의 대부분의 단점에 대해 훨씬 더 나은 해결책을 찾았습니다.

질문의 특정 문제를 해결하기 위해 내부적으로 [play-json-extensions]를 사용합니다.

여기에는 시리얼라이저/디시리얼라이저에 누락된 기본값이 자동으로 포함되어 리팩터에 오류가 발생하기 쉬운 매크로가 포함되어 있습니다.

import play.json.extra.Jsonx
implicit def jsonFormat = Jsonx.formatCaseClass[Foo]

라이브러리에는 더 많은 정보가 있습니다: play-json-filename

Json 변압기

현재 솔루션은 JSON Transformer를 생성하여 매크로에서 생성된 Reads와 결합하는 것입니다.트랜스폼은 다음 방법으로 생성됩니다.

object JsonExtensions{
  def withDefault[A](key:String, default:A)(implicit writes:Writes[A]) = __.json.update((__ \ key).json.copyFrom((__ \ key).json.pick orElse Reads.pure(Json.toJson(default))))
}

포맷 정의는 다음과 같습니다.

implicit val fooformats: Format[Foo] = new Format[Foo]{
  import JsonExtensions._
  val base = Json.format[Foo]
  def reads(json: JsValue): JsResult[Foo] = base.compose(withDefault("status","bidon")).reads(json)
  def writes(o: Foo): JsValue = base.writes(o)
}

그리고.

Json.parse("""{"id":"1", "value":"foo"}""").validate[Foo]

는 기본값을 적용한 Foo 인스턴스를 생성합니다.

제 의견에는 크게 두 가지 결함이 있습니다.

  • 채무 불이행자 키 이름이 문자열에 있으므로 리팩터링에 의해 선택되지 않습니다.
  • 기본값은 중복되며 한 위치에서 변경할 경우 다른 위치에서 수동으로 변경해야 합니다.

제가 찾은 가장 깨끗한 접근법은 "또는 순수"를 사용하는 것입니다.

...      
((JsPath \ "notes").read[String] or Reads.pure("")) and
((JsPath \ "title").read[String] or Reads.pure("")) and
...

이는 기본값이 상수일 때 일반적인 암묵적인 방법으로 사용할 수 있습니다.다이내믹한 경우에는 Reads를 작성하는 메서드를 작성하여 범위 내에서 도입해야 합니다.

implicit val packageReader = makeJsonReads(jobId, url)

An alternative solution is to use 대체 해결책은 다음과 같습니다.formatNullable[T] combined with 와 조합하여inmap부에서InvariantFunctor....

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

implicit val fooFormats = 
  ((__ \ "id").format[Int] ~
   (__ \ "value").format[String] ~
   (__ \ "status").formatNullable[String].inmap[String](_.getOrElse("pending"), Some(_))
  )(Foo.apply, unlift(Foo.unapply))

이제 Play Json 2.6과 함께 제공되는 With Default Values를 사용하는 것이 공식 답변은 다음과 같습니다.

implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]

편집:

동작은 play-json-extra 라이브러리와 다르다는 점에 주의해 주십시오.예를 들어, DateTime의 기본값을 가진 DateTime 매개 변수가 있는 경우.이제 프로세스의 시작 시간을 얻을 수 있습니다(아마 원하는 것은 아닐 것입니다). 반면 play-json-extra에서는 JSON에서 생성 시간을 얻을 수 있습니다.

모든 JSON 필드를 옵션(사용자 측에서는 옵션)으로 하고 싶지만, 내부적으로는 사용자가 특정 필드를 지정하지 않을 경우에 대비하여 모든 필드를 옵션 이외의 값으로 정확하게 정의해야 합니다.이것은 사용 사례와 비슷해야 합니다.

는 나 재 현 축 순 무 는 법 려 다있고 i consid'ruction고 wraps of방하짓 which마을단 the건 currently리m을히 const simplyeringFoo선택적 주장으로:완전 옵션 인수:

case class Foo(id: Int, value: String, status: String)

object FooBuilder {
  def apply(id: Option[Int], value: Option[String], status: Option[String]) = Foo(
    id     getOrElse 0, 
    value  getOrElse "nothing", 
    status getOrElse "pending"
  )
  val fooReader: Reads[Foo] = (
    (__ \ "id").readNullable[Int] and
    (__ \ "value").readNullable[String] and
    (__ \ "status").readNullable[String]
  )(FooBuilder.apply _)
}

implicit val fooReader = FooBuilder.fooReader
val foo = Json.parse("""{"id": 1, "value": "foo"}""")
              .validate[Foo]
              .get // returns Foo(1, "foo", "pending")

Unfortunately, it requires writing explicit 불행히도, 그것은 명시적인 쓰기를 필요로 한다.Reads[Foo] ★★★★★★★★★★★★★★★★★」Writes[Foo]마마피 하었 ?었? 었??? ???은 키가 이 "Default"인 입니다.null이 포함되어 " ", " "가 반환됩니다.ValidationError

이러한 옵션의 JSON 구조를 네스트 하는 것은 문제가 되지 않습니다.예를 들어 다음과 같습니다.

case class Bar(id1: Int, id2: Int)

object BarBuilder {
  def apply(id1: Option[Int], id2: Option[Int]) = Bar(
    id1     getOrElse 0, 
    id2     getOrElse 0 
  )
  val reader: Reads[Bar] = (
    (__ \ "id1").readNullable[Int] and
    (__ \ "id2").readNullable[Int]
  )(BarBuilder.apply _)
  val writer: Writes[Bar] = (
    (__ \ "id1").write[Int] and
    (__ \ "id2").write[Int]
  )(unlift(Bar.unapply))
}

case class Foo(id: Int, value: String, status: String, bar: Bar)

object FooBuilder {
  implicit val barReader = BarBuilder.reader
  implicit val barWriter = BarBuilder.writer
  def apply(id: Option[Int], value: Option[String], status: Option[String], bar: Option[Bar]) = Foo(
    id     getOrElse 0, 
    value  getOrElse "nothing", 
    status getOrElse "pending",
    bar    getOrElse BarBuilder.apply(None, None)
  )
  val reader: Reads[Foo] = (
    (__ \ "id").readNullable[Int] and
    (__ \ "value").readNullable[String] and
    (__ \ "status").readNullable[String] and
    (__ \ "bar").readNullable[Bar]
  )(FooBuilder.apply _)
  val writer: Writes[Foo] = (
    (__ \ "id").write[Int] and
    (__ \ "value").write[String] and
    (__ \ "status").write[String] and
    (__ \ "bar").write[Bar]
  )(unlift(Foo.unapply))
}

" 요건을 만, 를 "최소한의 노이즈"로 하는 것은 요?Option[String]

case class Foo(id:String,value:String, status:Option[String] = Some("pending"))

를 때Foo오래된 고객으로부터는None에 제가 ) getOrElse를 참조해 주세요.

마음에 안 들면 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아BackwardsCompatibleFoo:

case class BackwardsCompatibleFoo(id:String,value:String, status:Option[String] = "pending")
case class Foo(id:String,value:String, status: String = "pending")

그리고 나서 저걸 다른 걸로 바꿔서Foo코드에서 이런 종류의 데이터 체조를 다루지 않아도 되기 때문에 더 많은 작업을 할 수 있습니다.

상태를 옵션으로 정의할 수 있습니다.

case class Foo(id:String, value:String, status: Option[String])

다음과 같이 JsPath를 사용합니다.

(JsPath \ "gender").readNullable[String]

언급URL : https://stackoverflow.com/questions/20616677/defaults-for-missing-properties-in-play-2-json-formats

반응형