在 Play for Scala(版本 2.3.x)中解析特殊字符(例如 è、é 和 ®)JSON
Parsing special characters (e.g., è, é, and ®) JSONs in Play for Scala (version 2.3.x)
我在 Play for Scala(版本 2.3.x)中解析特殊字符(例如 è、é 和 ®)JSON 时遇到问题。由于 Play 中的所有内容都是 UTF-8 编码的,因此上述示例(è、é 和 ®)应该不成问题。我应该提到我正在使用 IntelliJ 和 sbt 进行开发。
我已验证以下内容:
- 测试文件是 UTF-8 编码的
- javac 使用 utf-8 (javacOptions ++= Seq("-encoding", "UTF-8"))
- 在 header 上设置 utf-8(application/json;字符集=utf-8)
- parse.tolerantJson(request)是错误的罪魁祸首(见下面代码)
测试文件:
{"id":"295b1590-220a-11e4-b641-90e08f355b68","blah":"è é ®"}
控制器:
def putGuid(streamId: String, guid: UUID) = Action.async(parseToEventContent) {
implicit request =>
render.async {
case mimeType@(Accepts.Json()) =>
val id = msgId.next()
(request.body match {
case JsonEventContent(j) =>
eventActor ? SendEventMessages.SendJsonEvent(id, streamId, Some(guid), remoteAddress(request), j)
case ... some other cases...
}) map {
case SendEventMessages.Done(`id`) => NoContent
case ... more error cases.. blah blah ...
case SendEventMessages.Unknown(`id`) =>
Logger.info("Message ID: {}. Received an unknown from underlying actors", id.asInstanceOf[AnyRef])
ServiceUnavailableRetryAfter()
} recover exceptionErrorHandler
} }
BodyParser 所在位置:
trait EventContent {}
case class JsonEventContent(data: JsValue) extends EventContent
def parseToEventContent: BodyParser[EventContent] = BodyParser("eventContent") {
request =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
request.contentType match {
case Some(MimeTypes.JSON) => parse.tolerantJson(request).map(_.right.map(JsonEventContent(_)))
case _ => Done(Left(UnsupportedMediaType), Empty)
}
}
编辑#1
在我的测试中使用类似下面的东西给我“èé®”的 400 个错误,但是 "abc" 的 204 个错误(我的源文件是 utf-8 并且 javac 设置为在 build.sbt):
val eventC = Json.parse(new String(s"""{"id":"295b1590-220a-11e4-b641-90e08f355b68","blah":"è é ®"}""".getBytes("UTF-8"), "UTF-8"))
val eventCJson = eventC.toString().getBytes("UTF-8")
"return 204 when entity has è, é, ® in json event" in new WithApplication(MyFakeApplication()){
println(s"json=${eventC}")
val result = assertPutRequest(s"/streams/com.gilt.clickstream/events/295b1590-220a-11e4-b641-90e08f355b68", 204, event = eventCJson, "application/json", "application/json; charset=utf-8")
contentType(result) must be equalTo None
contentAsBytes(result) must be equalTo Array()
}
def assertPutRequest(url: String, httpStatus: Int, event: Array[Byte], acceptHeader: String = "application/json", contentTypeHeader: String = "application/json; charset=utf-8")(implicit app: Application) = {
Akka.system.actorOf(MockEventActor.props, "event")
val result = route(
FakeRequest(PUT, url)
.withHeaders(
"Accept" -> acceptHeader,
"Content-Type" -> contentTypeHeader)
.withRawBody(event)
)(new Writeable({ j: AnyContentAsRaw => j.raw.asBytes().get}, None)).get
status(result) must be equalTo httpStatus
result
}
解决此问题的方法是升级到 JDK 1.7。我使用的是 JDK 1.6,这导致了问题。
我在 Play for Scala(版本 2.3.x)中解析特殊字符(例如 è、é 和 ®)JSON 时遇到问题。由于 Play 中的所有内容都是 UTF-8 编码的,因此上述示例(è、é 和 ®)应该不成问题。我应该提到我正在使用 IntelliJ 和 sbt 进行开发。
我已验证以下内容:
- 测试文件是 UTF-8 编码的
- javac 使用 utf-8 (javacOptions ++= Seq("-encoding", "UTF-8"))
- 在 header 上设置 utf-8(application/json;字符集=utf-8)
- parse.tolerantJson(request)是错误的罪魁祸首(见下面代码)
测试文件:
{"id":"295b1590-220a-11e4-b641-90e08f355b68","blah":"è é ®"}
控制器:
def putGuid(streamId: String, guid: UUID) = Action.async(parseToEventContent) {
implicit request =>
render.async {
case mimeType@(Accepts.Json()) =>
val id = msgId.next()
(request.body match {
case JsonEventContent(j) =>
eventActor ? SendEventMessages.SendJsonEvent(id, streamId, Some(guid), remoteAddress(request), j)
case ... some other cases...
}) map {
case SendEventMessages.Done(`id`) => NoContent
case ... more error cases.. blah blah ...
case SendEventMessages.Unknown(`id`) =>
Logger.info("Message ID: {}. Received an unknown from underlying actors", id.asInstanceOf[AnyRef])
ServiceUnavailableRetryAfter()
} recover exceptionErrorHandler
} }
BodyParser 所在位置:
trait EventContent {}
case class JsonEventContent(data: JsValue) extends EventContent
def parseToEventContent: BodyParser[EventContent] = BodyParser("eventContent") {
request =>
import play.api.libs.iteratee.Execution.Implicits.trampoline
request.contentType match {
case Some(MimeTypes.JSON) => parse.tolerantJson(request).map(_.right.map(JsonEventContent(_)))
case _ => Done(Left(UnsupportedMediaType), Empty)
}
}
编辑#1
在我的测试中使用类似下面的东西给我“èé®”的 400 个错误,但是 "abc" 的 204 个错误(我的源文件是 utf-8 并且 javac 设置为在 build.sbt):
val eventC = Json.parse(new String(s"""{"id":"295b1590-220a-11e4-b641-90e08f355b68","blah":"è é ®"}""".getBytes("UTF-8"), "UTF-8"))
val eventCJson = eventC.toString().getBytes("UTF-8")
"return 204 when entity has è, é, ® in json event" in new WithApplication(MyFakeApplication()){
println(s"json=${eventC}")
val result = assertPutRequest(s"/streams/com.gilt.clickstream/events/295b1590-220a-11e4-b641-90e08f355b68", 204, event = eventCJson, "application/json", "application/json; charset=utf-8")
contentType(result) must be equalTo None
contentAsBytes(result) must be equalTo Array()
}
def assertPutRequest(url: String, httpStatus: Int, event: Array[Byte], acceptHeader: String = "application/json", contentTypeHeader: String = "application/json; charset=utf-8")(implicit app: Application) = {
Akka.system.actorOf(MockEventActor.props, "event")
val result = route(
FakeRequest(PUT, url)
.withHeaders(
"Accept" -> acceptHeader,
"Content-Type" -> contentTypeHeader)
.withRawBody(event)
)(new Writeable({ j: AnyContentAsRaw => j.raw.asBytes().get}, None)).get
status(result) must be equalTo httpStatus
result
}
解决此问题的方法是升级到 JDK 1.7。我使用的是 JDK 1.6,这导致了问题。