当值为None时,在play-json序列化中输出Option [T]的'null'

时间:2014-03-05 07:43:45

标签: json scala playframework-2.0

我正在使用play-json的宏来定义用于序列化JSON的隐式Writes。但是,默认情况下,play-json似乎省略了Option字段设置为None的字段。有没有办法更改默认值,以便输出null?我知道如果我定义自己的Writes定义,这是可能的,但我有兴趣通过宏来减少样板代码。

示例

case class Person(name: String, address: Option[String])

implicit val personWrites = Json.writes[Person]    
Json.toJson(Person("John Smith", None))

// Outputs: {"name":"John Smith"}
// Instead want to output: {"name":"John Smith", "address": null}

7 个答案:

答案 0 :(得分:16)

Json.writes宏为可选字段生成writeNullable[T]。就像你知道(或不知道)一样,如果值为None,则writeNullable[T]省略该字段,而write[Option[T]]生成一个空字段。

定义自定义编写器是获得此行为的唯一选择。

( 
  (__ \ 'name).write[String] and
  (__ \ 'address).write[Option[String]]
)(unlift(Person.unapply _))

答案 1 :(得分:6)

对你来说不是真正的解决方案。但稍微好于手动写入写入

我创建了一个帮助类,可以确保"领域。

implicit class WritesOps[A](val self: Writes[A]) extends AnyVal {
    def ensureField(fieldName: String, path: JsPath = __, value: JsValue = JsNull): Writes[A] = {
      val update = path.json.update(
        __.read[JsObject].map( o => if(o.keys.contains(fieldName)) o else o ++ Json.obj(fieldName -> value))
      )
      self.transform(js => js.validate(update) match {
        case JsSuccess(v,_) => v
        case err: JsError => throw new JsResultException(err.errors)
      })
    }

    def ensureFields(fieldNames: String*)(value: JsValue = JsNull, path: JsPath = __): Writes[A] =
      fieldNames.foldLeft(self)((w, fn) => w.ensureField(fn, path, value))

}

这样你就可以写

Json.writes[Person].ensureFields("address")()

答案 2 :(得分:0)

上面的类似答案,但另一种语法:

implicit val personWrites = new Writes[Person] {
  override def writes(p: Person) = Json.obj(
    "name" -> p.name,
    "address" -> p.address,
  )
}

答案 3 :(得分:0)

这很简单:

implicit val personWrites = new Writes[Person] {
  override def writes(p: Person) = Json.obj(
    "name" -> p.name,
    "address" -> noneToString(p.address),
  )
}

def optToString[T](opt: Option[T]) = 
   if (opt.isDefined) opt.get.toString else "null"

答案 4 :(得分:0)

您可以定义如下内容:

    login.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String emailString =email.getText().toString();
            String pwd =password.getText().toString();

            if(!emailString.equals("")&& !pwd.equals("")){
                mAuth.signInWithEmailAndPassword(emailString,pwd)
                        .addOnCompleteListener(MainActivity.this, new OnCompleteListener<AuthResult>() {
                            @Override
                            public void onComplete(@NonNull Task<AuthResult> task) {
                                if(!task.isSuccessful()){
                                    Toast.makeText(MainActivity.this,"Unrecognised Credentials",Toast.LENGTH_LONG).show();
                                } else { Toast.makeText  
          (MainActivity.this,"recognised  Credentials",Toast.LENGTH_LONG).show();  

                                }
                            }
                        });
            }

    @Override
    protected void onStart() {
    super.onStart();
    mAuth.addAuthStateListener(mAuthListener);


    }

    @Override
    protected void onStop() {
    super.onStop();

    if(mAuthListener != null){
        mAuth.removeAuthStateListener(mAuthListener);
     }
     }
     }

如果您正在使用Play框架:

  implicit class JsPathExtended(path: JsPath) {
    def writeJsonOption[T](implicit w: Writes[T]): OWrites[Option[T]] = OWrites[Option[T]] { option =>
      option.map(value =>
        JsPath.createObj(path -> w.writes(value))
      ).getOrElse(JsPath.createObj(path -> JsNull))
    }
  }

答案 5 :(得分:0)

您可以包装您的选项并重新定义序列化行为:

case class MyOption[T](o: Option[T])
implicit def myOptWrites[T: Writes] = Writes[MyOption[T]](_.o.map(Json.toJson).getOrElse(JsNull))

CAVEATS:

1)这种方法仅适用于仅用作序列化协议定义的案例类。如果要在控制器中重用某些数据模型类 - 为它们定义自定义序列化程序,以免污染模型。

2)(仅与Option相关)如果您使用相同的类进行写入并读取。播放将要求在反序列化期间存在包装的字段(可能为空)。

P.S。:无法对类型标记执行相同操作。编译器错误就像 No instance of play.api.libs.json.Writes is available for tag.<refinement>(已明确定义所需的写入)。 看起来像Play的宏故障。

答案 6 :(得分:0)

您可以使用自定义的隐式 JsonConfiguration ,请参见Customize the macro to output null

implicit val config = JsonConfiguration(optionHandlers = OptionHandlers.WritesNull)

implicit val personWrites = Json.writes[Person]    
Json.toJson(Person("John Smith", None))