将circe中json对象的所有键从“下划线”转换为“驼峰大小写”
起源 期望将circe中json对象的所有键从“下划线”转换为“驼峰大小写”,json,scala,circe,Json,Scala,Circe,起源 期望 { "first_name" : "foo", "last_name" : "bar", "parent" : { "first_name" : "baz", "last_name" : "bazz", } } 如何转换json对象的所有键?def transformKeys(json:json,f:String=>String):TailRec[json]={ { "firstName" : "foo", "lastNam
{
"first_name" : "foo",
"last_name" : "bar",
"parent" : {
"first_name" : "baz",
"last_name" : "bazz",
}
}
如何转换json对象的所有键?def transformKeys(json:json,f:String=>String):TailRec[json]={
{
"firstName" : "foo",
"lastName" : "bar",
"parent" : {
"firstName" : "baz",
"lastName" : "bazz",
}
}
if(json.isObject){
val obj=json.asObject.get
val fields=obj.toList.foldLeft(完成(List.empty[(String,Json)]){(r,kv)=>
val(k,v)=kv
为了{
fs Json.obj(fs:*)
}else if(json.isArray){
val arr=json.asArray.get
val vsRec=arr.foldLeft(完成(List.empty[Json]){(vs,v)=>
为了{
以下是我写这篇文章的方式。它没有我想要的那么简洁,但也不可怕:
def transformKeys(json: Json, f: String => String): TailRec[Json] = {
if(json.isObject) {
val obj = json.asObject.get
val fields = obj.toList.foldLeft(done(List.empty[(String, Json)])) { (r, kv) =>
val (k, v) = kv
for {
fs <- r
fv <- tailcall(transformKeys(v, f))
} yield fs :+ (f(k) -> fv)
}
fields.map(fs => Json.obj(fs: _*))
} else if(json.isArray) {
val arr = json.asArray.get
val vsRec = arr.foldLeft(done(List.empty[Json])) { (vs, v) =>
for {
s <- vs
e <- tailcall(transformKeys(v, f))
} yield s :+ e
}
vsRec.map(vs => Json.arr(vs: _*))
} else {
done(json)
}
}
然后:
import cats.free.Trampoline
import cats.std.list._
import cats.syntax.traverse._
import io.circe.{ Json, JsonObject }
/**
* Helper method that transforms a single layer.
*/
def transformObjectKeys(obj: JsonObject, f: String => String): JsonObject =
JsonObject.fromIterable(
obj.toList.map {
case (k, v) => f(k) -> v
}
)
def transformKeys(json: Json, f: String => String): Trampoline[Json] =
json.arrayOrObject(
Trampoline.done(json),
_.traverse(j => Trampoline.suspend(transformKeys(j, f))).map(Json.fromValues),
transformObjectKeys(_, f).traverse(obj => Trampoline.suspend(transformKeys(obj, f))).map(Json.fromJsonObject)
)
最后:
import io.circe.literal._
val doc = json"""
{
"first_name" : "foo",
"last_name" : "bar",
"parent" : {
"first_name" : "baz",
"last_name" : "bazz"
}
}
"""
def sc2cc(in: String) = "_([a-z\\d])".r.replaceAllIn(in, _.group(1).toUpperCase)
我们可能应该有某种方法递归地应用这样的Json=>F[Json]
转换,这样更方便。我接受了@Travis answer并对其进行了一点现代化,我接受了他的代码,并且出现了一些错误和警告,因此,使用Cats 1.0.0-MF的Scala 2.12的更新版本:
scala> import cats.std.function._
import cats.std.function._
scala> transformKeys(doc, sc2cc).run
res0: io.circe.Json =
{
"firstName" : "foo",
"lastName" : "bar",
"parent" : {
"firstName" : "baz",
"lastName" : "bazz"
}
}
根据您的完整用例,在最新的情况下,您可能更愿意利用现有的解码器/编码器,根据以下参考在驼峰/蛇之间进行转换:
例如,在我的特定用例中,这是有意义的,因为我正在执行其他操作,这些操作受益于首先反序列化为case类的类型安全性。因此,如果您愿意将JSON解码为case类,然后将其编码回JSON,那么您所需要的只是(de)序列化代码以扩展配置此特性的特性,如:
import io.circe.literal._
import cats.free.Trampoline, cats.instances.list._, cats.instances.function._, cats.syntax.traverse._, cats.instances.option._
def transformKeys(json: Json, f: String => String): Trampoline[Json] = {
def transformObjectKeys(obj: JsonObject, f: String => String): JsonObject =
JsonObject.fromIterable(
obj.toList.map {
case (k, v) => f(k) -> v
}
)
json.arrayOrObject(
Trampoline.done(json),
_.toList.traverse(j => Trampoline.defer(transformKeys(j, f))).map(Json.fromValues(_)),
transformObjectKeys(_, f).traverse(obj => Trampoline.defer(transformKeys(obj, f))).map(Json.fromJsonObject)
)
}
def sc2cc(in: String) = "_([a-z\\d])".r.replaceAllIn(in, _.group(1).toUpperCase)
def camelizeKeys(json: io.circe.Json) = transformKeys(json, sc2cc).run
例如,当我实际解析或输出JSON时,我扩展了它:
import io.circe.derivation._
import io.circe.{Decoder, Encoder, ObjectEncoder, derivation}
import io.circe.generic.auto._
import io.circe.parser.decode
import io.circe.syntax._
trait JsonSnakeParsing {
implicit val myCustomDecoder: Decoder[MyCaseClass] = deriveDecoder[MyCaseClass](io.circe.derivation.renaming.snakeCase)
// only needed if you want to serialize back to snake case json:
// implicit val myCustomEncoder: ObjectEncoder[MyCaseClass] = deriveEncoder[MyCaseClass](io.circe.derivation.renaming.snakeCase)
}
对于本例,您的案例类可能如下所示:
trait Parsing extends JsonSnakeParsing {
val result: MyCaseClass = decode[MyCaseClass](scala.io.Source.fromResource("my.json").mkString) match {
case Left(jsonError) => throw new Exception(jsonError)
case Right(source) => source
}
val theJson = result.asJson
}
以下是本例中circe依赖项的完整列表:
case class MyCaseClass(firstName: String, lastName: String, parent: MyCaseClass)
@Thilo是的,它的CAMECASE现在应该是一个被接受的答案。使用内置功能而不是自己实现是一个更简洁的方法吗?它似乎是一个常见的用例。
val circeVersion = "0.10.0-M1"
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-parser" % circeVersion,
"io.circe" %% "circe-generic-extras" % circeVersion,
"io.circe" %% "circe-derivation" % "0.9.0-M5",