Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
播放json:JsNull不等于JsString(null),如何回避这个问题?_Json_Scala_Play Json - Fatal编程技术网

播放json:JsNull不等于JsString(null),如何回避这个问题?

播放json:JsNull不等于JsString(null),如何回避这个问题?,json,scala,play-json,Json,Scala,Play Json,我正在处理一些大的JSON输出。我们有一系列的测试来检查输出。通过在磁盘上保存JSON副本,在InputStream上执行JSON.parse(),并将其与内存中构建的JsObject进行比较,我们创建了测试 在我开始将一些写入操作转换为使用函数语法之前,这种方法一直运行良好(即,使用构建器,而不是覆盖trait中的写入操作方法) 突然,测试开始失败:抱怨null字段不相等 显然,在使用函数语法时,选项[String]将转换为JsString(null),而不是JsNull。这在字符串化版本中不

我正在处理一些大的JSON输出。我们有一系列的测试来检查输出。通过在磁盘上保存JSON副本,在
InputStream
上执行
JSON.parse()
,并将其与内存中构建的JsObject进行比较,我们创建了测试

在我开始将一些
写入操作
转换为使用函数语法之前,这种方法一直运行良好(即,使用构建器,而不是覆盖trait中的
写入操作
方法)

突然,测试开始失败:抱怨
null
字段不相等

显然,在使用函数语法时,
选项[String]
将转换为
JsString(null)
,而不是
JsNull
。这在字符串化版本中不明显

考虑以下代码段,使用

libraryDependencies += "com.typesafe.play" %% "play-json" % "2.7.4"
import play.api.libs.json_
导入play.api.libs.functional.syntax_
对象FooBar{
案例类Foo(选项:选项[String])
def main(参数:数组[字符串]):单位={
val classic:OWrites[Foo]=新的OWrites[Foo]{
重写def写入(f:Foo):JsObject=Json.obj(“Foo”->f.option)
}
val dsl:OWrites[Foo]=(\uuuu\“Foo”).write[String].contramap(Foo=>Foo.option.orNull)
val json_classic=json.toJsObject(Foo(None))(classic)
val json_dsl=json.toJsObject(Foo(None))(dsl)
val json_parse=json.parse(“{”foo:null}”)
val string_classic=Json.prettyPrint(Json_classic)
val string_dsl=Json.prettyPrint(Json_dsl)
普林顿(
结果是:
|json_dsl==json_classic:${json_dsl==json_classic}//(预期为真)
|json_dsl==json_parse:${json_dsl==json_parse}//(预期为真)
|json_classic==json_parse:${json_classic==json_parse}//(预期为真)
|string_classic==string_dsl:${string_classic==string_dsl}//(期望为true)
|“.stripMargin)
println(s“classic:\n$string\u classic”)
println(s“dsl:\n$string\u dsl”)
}
}
实际产量为

Result is:
json_dsl       == json_classic : false // (expect true)
json_dsl       == json_parse   : false // (expect true)
json_classic   == json_parse   : true // (expect true)
string_classic == string_dsl   : true // (expect true)

classic:
{
  "foo" : null
}
dsl:
{
  "foo" : null
}
在调试时,您将看到经典的方法使用Tuple
(“foo”,JsNull)
创建包装器对象,而dsl使用Tuple
(“foo”,JsString(null))
创建包装器

在这种情况下,dsl的预期方式似乎是使用
writeNullable
,但这种方式让人感到奇怪

我希望
JsString(null)==JsNull
为真,或者dsl将捕获
null
值并阻止创建
JsString

我是在做一些完全被误导的事情吗

我只想重写为
.writeNullable[String]
,这将从JSON中删除该字段,但我们有一个要求该字段存在的模式:

...
"properties": {
  ...
  "foo": {
    "oneOf": [
      {"type": "string"},
      {"type": "null"}
    ]
  },
  ...
}
...
"required": [ "foo" ],
这是API的一部分,因此更改它需要时间


澄清一下:字符串表示在所有情况下都是正确的。我只对
JsValue
的内存表示感兴趣,这样我就可以在测试过程中使用它的相等性。

我无法使用Play JSON 2.7.4在REPL中重现您的测试:

import play.api.libs.json._

case class Foo(option: Option[String])

val classic: OWrites[Foo] = new OWrites[Foo] {
  override def writes(f: Foo): JsObject = Json.obj("foo" -> f.option)
}

val dsl: OWrites[Foo] = (__ \ "foo").write[String].contramap(foo => foo.option.orNull)


val json_classic = Json.toJsObject(Foo(None))(classic)
val json_dsl = Json.toJsObject(Foo(None))(dsl)

val json_classic = Json.toJsObject(Foo(None))(classic)
// json_classic: play.api.libs.json.JsObject = {"foo":null}

val json_dsl = Json.toJsObject(Foo(None))(dsl)
// json_dsl: play.api.libs.json.JsObject = {"foo":null}
json_classic
json_dsl
之间的不平等是另一回事,但json表示在这两种情况下是一致的,即使
foo.option.orNull对我来说是不安全的/奇怪的

另一方面,如果您想考虑<代码>“null”<代码> >代码> null <代码>,您可以重写默认的<代码>读[String ] 。

scala> val legacyStrReads: Reads[Option[String]] =
     |   Reads.optionWithNull(Reads.StringReads).map {
     |     case Some("null") => None
     |     case other => other
     |   }
legacyStrReads: play.api.libs.json.Reads[Option[String]] = play.api.libs.json.Reads$$anon$6@138decb1

scala> Json.toJson("null").validate(legacyStrReads)
res9: play.api.libs.json.JsResult[Option[String]] = JsSuccess(None,)

我想你想要的是:

val-dsl:OWrites[Foo]=(\uuuu\“Foo”)。writeOptionWithNull[String]
.contramap(u.option)
或者,如果使用的是较旧的play json版本,该版本没有
WriteOptions WithNull

val-dsl:OWrites[Foo]=(_u\“Foo”)。写入[JsValue]
.contramap(u.option匹配){
case None=>JsNull
case Some(string)=>JsString(string)
})
请注意,
play.api.libs.json.JsNull
null
是两个完全不同且不相关的概念,决不能混淆或混淆。在Scala-only API中,整个Scala生态系统的约定是永远不要使用
null
,因为大多数库只是假装它根本不存在。因此,您不会发现很多Scala库执行空检查,它们只是假设所有内容都是非空的,而一旦您开始使用空,您就处于未测试和不支持的边缘情况的西部。您使用或处理空值的唯一时间是在使用Java API时,因为它们使用空值,而且那里的惯例是尽可能早地在
选项
中包装任何可以产生
null
的内容,并尽可能晚地使用
orNull
打开
选项
,因此,至少在不直接与Java代码接口的Scala代码内部,所有内容都使用
选项
,而不是
null
。但是play json是专为Scala使用而设计的,因此与Scala only生态系统的其他部分一样,它只是假设
null
不存在


当然,在json中,
null
确实存在,使用它可能有正当的理由(特别是在与其他需要它的系统集成时)。所以play json确实对它进行了建模,但它以一种非常强类型的方式对它进行了建模——在Scala中没有任何东西会自动从
null
变为
null
,在json中,它总是非常显式的,这与Scala的强类型一致。另外,在JSON中使用
null
并不常见,因此大多数默认方法(即
writeNullable
方法)将
选项映射到一个不存在的值,因此在编写JSON
null
时,必须更加明确一点,如上所示。

p.S.:我知道我可以在这个例子中使用经典的符号,这样它就可以做它想做的事情