【问题标题】:How can I ignore empty objects with play-json?如何使用 play-json 忽略空对象?
【发布时间】:2019-04-24 10:15:25
【问题描述】:

我是 Scala 和函数式编程的新手,所以这个问题可能有一个非常简单的答案。但是,我无法弄清楚,所以这里是:

我有一个案例类层次结构,它表示编译器中的抽象语法树。在某些时候,我有一个List[Statement] 形式的语句列表。这将使用 Json 对象数组进行序列化,其中每个对象代表一个语句。但是,有些语句是多余的,所以我不想序列化它们。例如,返回 void。以下代码有望澄清这一点:

sealed trait Statement

sealed trait Expression

case object Empty extends Expression

case class Return(e: Expression) extends Statement

我想要的是根本不序列化 Return(Empty)。现在我得到类似的东西:

"Return": {}

我怎样才能简单地忽略 Return(Empty) ? 使其更通用。我不想像{} 这样的json 中有空对象。我如何确保他们不会进入我的 json?

这是我迄今为止尝试过的(但没有奏效):

implicit val statementListWrites = StatementListWrites

object StatementListWrites extends Writes[List[Statement]] {
  override def writes(stms: List[Statement]) =
    JsArray(stms.filter(stm => {
      stm match {
        case Return(Empty) => false
        case _ => true
      }
    }).map(s => Json.toJson(s)))
}

也许上述技术有效,但由于某种原因,据我所知,它甚至没有被称为开始。

【问题讨论】:

    标签: scala serialization playframework case-class play-json


    【解决方案1】:

    在这种情况下你应该小心,因为你有密封的特质,所以你应该有条不紊地工作。

    让我们将问题分解为更小的问题,假设您只想将对象写入 json。那么我们就可以解决删除空对象的问题了。

    我添加了更多类来阐明这个想法:

    sealed trait Expression
    case object Empty extends Expression
    case class NonEmptyExpression(x: Int) extends Expression
    
    sealed trait Statement
    case class Return(e: Expression) extends Statement
    case class NoReturn(x: Int) extends Statement
    

    第一步(为表达式提供编写器):

    为此,您必须为EmptyNonEmptyExpression 创建作家,所以我们可以这样做:

    val emptyExpressionWrites = new Writes[Empty.type] {
      override def writes(e: Empty.type) = Json.obj()
    }
    
    val nonEmptyExpressionWrites = Json.writes[NonEmptyExpression]
    
    implicit val expressionWrites = new Writes[Expression] {
      override def writes(exp: Expression) = {
        exp match {
          case Empty => emptyExpressionWrites.writes(Empty)
          case nonEmptyExpression: NonEmptyExpression => nonEmptyExpressionWrites.writes(nonEmptyExpression)
        }
      }
    }
    

    第二步(为语句提供作者):

    您必须为ReturnNoReturn 提供作家。请注意,play 能够知道如何为Return 创建作家,并且它有Expression,因为我将expressionWriter 定义为implicit。所以它现在知道如何序列化Expression

    val returnWrites = Json.writes[Return]
    val noReturnWrites = Json.writes[NoReturn]
    
    val statementWrites = new Writes[Statement] {
      override def writes(s: Statement) = {
        s match {
          case r: Return => returnWrites.writes(r)
          case nr: NoReturn => noReturnWrites.writes(nr)
        }
      }
    }
    

    让我们测试一下,确保它正常工作:

    val statementWithNonEmpty = Return(NonEmptyExpression(100))
    println(s"statementWithNonEmpty Json: ${statementWrites.writes(statementWithNonEmpty).toString()}")
    
    val statementWithEmpty = Return(Empty)
    println(s"statementWithEmpty Json: ${statementWrites.writes(statementWithEmpty).toString()}")
    

    输出是:

    statementWithNonEmpty Json: {"e":{"x":100}}

    statementWithEmpty Json: {"e":{}}

    最后一步(创建没有 Empty 的 List writer):

    val listStatementWrites = new Writes[Seq[Statement]] {
      override def writes(o: Seq[Statement]) = {
        val listWithoutEmpty = o.filter {
          case Return(Empty) => false
          case _ => true
        }
    
        JsArray(listWithoutEmpty.map(statementWrites.writes))
      }
    }
    

    让我们试试吧:

    val listOfStatements = List(Return(NonEmptyExpression(100)), Return(Empty), Return(NonEmptyExpression(200)))
    println(s"listOfStatements: ${listStatementWrites.writes(listOfStatements).toString()}")
    

    输出是:

    listOfStatements: [{"e":{"x":100}},{"e":{"x":200}}]

    【讨论】:

    • 感谢您的详细回复。但是,如果您仔细阅读我的问题,您会注意到我尝试的方法与您最后的建议非常接近,对吧?所以我不确定你为什么要检查所有其他的东西:)。请问您能否详细说明为什么您的方法有效而我的方法无效?我很好奇为什么你必须使用 Seq 而不是 List,以及为什么你通过 statementWrites.writes 而不是 Json.toJson。后者不会在内部调用前者吗?
    • 你尝试的和我回答的不一样。我太冗长了,因为这样的情况并非微不足道,简短的回答可能还不够。
    • 如果您看到 Json.toJson 需要 Writes[Statement] 作为隐式参数,而您的代码中没有它。
    猜你喜欢
    • 2023-03-15
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2019-03-21
    • 2011-12-19
    • 2017-02-16
    • 2017-11-11
    相关资源
    最近更新 更多