我正在使用Finch,Circe和Sangria为API构建GraphQL端点。在GraphQL查询中遇到的variables
基本上是一个任意的JSON对象(我们假设没有嵌套)。例如,在我的测试代码String
中,这里有两个例子:
val variables = List(
"{\n \"foo\": 123\n}",
"{\n \"foo\": \"bar\"\n}"
)
Sangria API需要Map[String, Any]
的类型。
我尝试了很多方法,但到目前为止还未能在Circe中为此写Decoder
。任何帮助表示赞赏。
答案 0 :(得分:5)
Sangria API需要Map [String,Any]
的类型
事实并非如此。在sangria中执行的变量可以是任意类型getLocation: 150.0 100.0
getLocationOnScreen: 150.0 100.0
,这是唯一要求它具有T
类型类实例的要求。所有编组集成库都为对应的JSON AST类型提供InputUnmarshaller[T]
的实例。
这意味着sangria-circe定义了InputUnmarshaller
,您可以使用InputUnmarshaller[io.circe.Json]
导入它。
以下是一个小型且自包含的示例,说明如何将circe import sangria.marshalling.circe._
用作变量:
Json
正如您在此示例中所看到的,我使用import io.circe.Json
import sangria.schema._
import sangria.execution._
import sangria.macros._
import sangria.marshalling.circe._
val query =
graphql"""
query ($$foo: Int!, $$bar: Int!) {
add(a: $$foo, b: $$bar)
}
"""
val QueryType = ObjectType("Query", fields[Unit, Unit](
Field("add", IntType,
arguments = Argument("a", IntType) :: Argument("b", IntType) :: Nil,
resolve = c ⇒ c.arg[Int]("a") + c.arg[Int]("b"))))
val schema = Schema(QueryType)
val vars = Json.obj(
"foo" → Json.fromInt(123),
"bar" → Json.fromInt(456))
val result: Future[Json] =
Executor.execute(schema, query, variables = vars)
作为执行变量。执行将产生以下结果JSON:
io.circe.Json
答案 1 :(得分:1)
这是一个有效的解码器。
type GraphQLVariables = Map[String, Any]
val graphQlVariablesDecoder: Decoder[GraphQLVariables] = Decoder.instance { c =>
val variablesString = c.downField("variables").focus.flatMap(_.asString)
val parsedVariables = variablesString.flatMap { str =>
val variablesJsonObject = io.circe.jawn.parse(str).toOption.flatMap(_.asObject)
variablesJsonObject.map(j => j.toMap.transform { (_, value: Json) =>
val transformedValue: Any = value.fold(
(),
bool => bool,
number => number.toDouble,
str => str,
array => array.map(_.toString),
obj => obj.toMap.transform((s: String, json: Json) => json.toString)
)
transformedValue
})
}
parsedVariables match {
case None => left(DecodingFailure(s"Unable to decode GraphQL variables", c.history))
case Some(variables) => right(variables)
}
}
我们基本上解析JSON,将其转换为JsonObject
,然后相当简单地转换对象中的值。
答案 2 :(得分:0)
虽然上述答案适用于桑格利亚的具体情况,但我对原始问题感兴趣:什么是Circe的最佳方法(通常假设所有类型都是预先知道的)任意大块的Json?
在编码/解码Json时,相当常见的是指定了95%的Json,但最后5%是某种类型的"附加属性"块可以是任何Json对象。
我玩过的解决方案:
将自由格式块编码/解码为Map[String,Any]
。这意味着您必须为Map[String, Any]
引入隐式编码器/解码器,这可以完成,但是很危险,因为隐式编码器/解码器可以被引入您不想要的地方。
将自由格式块编码/解码为Map[String, Json]
。这是最简单的方法,并且在Circe中开箱即用。但是现在Json序列化逻辑已泄露到你的API中(通常你会想要保持Json的东西完全被包装,所以你可以在以后交换其他非json格式。)
对String
进行编码/解码,其中字符串必须是有效的Json块。至少你还没有将你的API锁定到一个特定的Json库中,但是要让用户以这种手动方式创建Json块并不是很好。
创建自定义特征层次结构以保存数据(例如sealed trait Free; FreeInt(i: Int) extends Free; FreeMap(m: Map[String, Free] extends Free; ...
)。现在您可以为它创建特定的编码器/解码器。但是你真正做的是复制已经存在于Circe中的Json类型层次结构。
我更倾向于选项3.因为它是最灵活的,并且会在API中引入最少的依赖项。但它们都不是完全令人满意的。还有其他想法吗?