如何在Scala Play应用中转换TypeScript传入的Uint8Array对象
如何在Scala Play应用中解析TypeScript发送的Uint8Array对象?
场景说明
TypeScript应用通过HTTP POST请求向Scala Play应用发送包含Uint8Array的对象,示例代码如下:
{stuff: new Uint8Array([0, 1, 2])}
原代码及报错
最初尝试用Play默认的JSON读取器解析,控制器代码如下:
case class StuffData(stuff: List[Byte]) implicit val reads: Reads[StuffData] = Json.reads[StuffData] def processStuff = Action.async(parse.json[StuffData]) { request => val stuffData = request.body println(stuffData) }
执行后Play返回如下错误:
For request 'POST /send-stuff'
[Json validation error List((obj.key,List(JsonValidationError(List(error.expected.jsarray),WrappedArray()))))]
解决方案
要在Scala端获取与JavaScript端完全一致的字节数组(逐字节保持原顺序),需自定义JSON读取器,代码如下:
case class StuffData(stuff: List[Byte]) implicit val stuffReader = new Reads[StuffData] { def reads(js: JsValue): JsResult[StuffData] = { JsSuccess(StuffData( (js \ "stuff").as[Map[String, Int]].toList.sortBy(_._1.toInt).map(_._2.toByte) )) } } def processStuff = Action.async(parse.json[StuffData]) { request => val stuffData = request.body println(stuffData) }
解析逻辑说明
- JavaScript的
Uint8Array序列化为JSON时,会转换为以索引为键、字节值为值的对象结构(而非数组),因此默认的数组读取器无法识别 - 自定义读取器先将该对象转为
Map[String, Int],再按索引键的数值排序,最后将Int值转为Byte(由于Uint8Array的取值范围是0-255,转换为Byte不会丢失信息),最终得到与原Uint8Array顺序一致的List[Byte]
内容的提问来源于stack exchange,提问作者rapt
相关产品推荐
相关产品推荐

