Play/Scala如何防止空数组的Json序列化?

Posted

技术标签:

【中文标题】Play/Scala如何防止空数组的Json序列化?【英文标题】:Play/Scala how to prevent Json serialization of empty arrays? 【发布时间】:2014-02-13 09:35:42 【问题描述】:

我想递归地写一个类到 Json,所以我使用以下隐式写入:

implicit val writesObject : Writes[Object] = (
  (__ \ "id").writeNullable[String] ~
  (__ \ "list").lazyWriteNullable(Writes.traversableWrites[Object](writesObject))
)(unlift(Object.unapply)

其中 Object 是这样的类:

case class Object(id: Option[String], list: Option[Seq[Object]])

它可以工作,但是如果“列表”为空,我想阻止它打印任何内容。例如:

我想要:

 id: "someID",
  list: [
          
            id: "someOtherId"            
          
        ] 

我目前得到(但不想要):

 id: "someID",
  list: [
          
            id: "someOtherId"            
            list: []
          
        ] 

我怎样才能做到这一点?我是 Play/Scala 的新手,不确定我应该看什么,所以任何指针都会有所帮助。我正在使用 Scala 2.2.1。

PS:我检查了Scala Json Combinators,但没有看到任何有关如何完成此操作的参考。

更新:

所以我的问题不是列表为空,而是列表为空。这就是为什么lazyWriteNullable 不起作用的原因。

Testing johanandren answer 我想出了以下 JsPath 扩展,它返回 Option[T] 并支持递归写入的惰性格式:

def lazyWriteNullableIterable[T <: Iterable[_]](w: => Writes[T]): OWrites[Option[T]] = OWrites((t: Option[T]) => 
  if(t != null) 
    t.getOrElse(Seq.empty).size match 
      case 0 => Json.obj()
      case _ => Writes.nullable[T](path)(w).writes(t)
    
  
  else 
    Json.obj()
  
)

谢谢

【问题讨论】:

【参考方案1】:

您可以创建一个自定义 OFormat 来执行此操作。通过用它隐式装饰 JsPath,您可以将它包含在您的 json 组合器定义中:

implicit class PathAdditions(path: JsPath) 

  def readNullableIterable[A <: Iterable[_]](implicit reads: Reads[A]): Reads[A] =
    Reads((json: JsValue) => path.applyTillLast(json).fold(
      error => error,
      result => result.fold(
        invalid = (_) => reads.reads(JsArray()),
        valid = 
          case JsNull => reads.reads(JsArray())
          case js => reads.reads(js).repath(path)
        )
    ))

  def writeNullableIterable[A <: Iterable[_]](implicit writes: Writes[A]): OWrites[A] =
    OWrites[A] (a: A) =>
      if (a.isEmpty) Json.obj()
      else JsPath.createObj(path -> writes.writes(a))
    

  /** When writing it ignores the property when the collection is empty,
    * when reading undefined and empty jsarray becomes an empty collection */
  def formatNullableIterable[A <: Iterable[_]](implicit format: Format[A]): OFormat[A] =
    OFormat[A](r = readNullableIterable(format), w = writeNullableIterable(format))


这将允许您使用 json 组合器语法创建格式/读取/写入,如下所示:

case class Something(as: List[String], v: String)

import somewhere.PathAdditions    
val reads: Reads[Something] = (
  (__ \ "possiblyMissing").readNullableIterable[List[String]] and
  (__ \ "somethingElse").read[String]
)(Something)

val writes: Writes[Something] = (
  (__ \ "possiblyMissing").writeNullableIterable[List[String]] and
  (__ \ "somethingElse").write[String]
)(unlift(Something.unapply))

val format: Format[Something] = (
  (__ \ "possiblyMissing").formatNullableIterable[List[String]] and
  (__ \ "somethingElse").format[String]
)(Something, unlift(Something.unapply))

【讨论】:

感谢您的帮助!我用我带来的最终方法更新了我的答案。我做了一些更改,因为我需要它返回 Option[T] 并且还需要惰性,否则由于循环隐式存在 NPE。 给出的答案对我帮助很大,直到最近我犯了“错误”,将它与一个可迭代的 Map 一起使用。地图需要一个空的 Json 对象而不是数组。因此,我将 readNullableIterable 转换为 def readNullableIterable[A &lt;: Iterable[_]](implicit reads: Reads[A], tag: TypeTag[A]): Reads[A] 之类的东西,以区分 Map 和 Iterable,例如 Seq 和 Set。

以上是关于Play/Scala如何防止空数组的Json序列化?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Json.Net 转换为 XML 时如何序列化空数组

将空 JSON 数组反序列化为空 TreeMap

如何使用 Rest Assured 在 Json 中构造空数组列表

iOS JSON 序列化空数组序列化为 nil 而不是 [ ]

如何防止 JsonWriter 在序列化多维数组时添加太多换行符?

如何防止 Hibernate 更新 NULL 值