我正在使用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}
答案 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))