在Scala中,代数数据类型被编码为sealed
一级类型层次结构.例:
-- Haskell
data Positioning a = Append
| AppendIf (a -> Bool)
| Explicit ([a] -> [a])
Run Code Online (Sandbox Code Playgroud)
// Scala
sealed trait Positioning[A]
case object Append extends Positioning[Nothing]
case class AppendIf[A](condition: A => Boolean) extends Positioning[A]
case class Explicit[A](f: Seq[A] => Seq[A]) extends Positioning[A]
Run Code Online (Sandbox Code Playgroud)
随着case class
ES和case object
S,斯卡拉产生了一堆东西一样equals
,hashCode
,unapply
(通过模式匹配使用)等这使我们的许多关键特性和传统的ADT功能.
但是有一个关键的区别 - 在Scala中,"数据构造函数"有自己的类型.比较以下两个例子(从相应的REPL复制).
// Scala
scala> :t Append
Append.type
scala> :t AppendIf[Int](Function const true)
AppendIf[Int]
-- Haskell
haskell> :t Append …
Run Code Online (Sandbox Code Playgroud) 我有这两个错误:
Error:(39, 20) Cannot find an implicit ExecutionContext. You might pass
an (implicit ec: ExecutionContext) parameter to your method
or import scala.concurrent.ExecutionContext.Implicits.global.
val pipeline = sendReceive
^
Error:(39, 20) not enough arguments for method sendReceive: (implicit refFactory: akka.actor.ActorRefFactory, implicit executionContext: scala.concurrent.ExecutionContext, implicit futureTimeout: akka.util.Timeout)spray.client.pipelining.SendReceive.
Unspecified value parameter executionContext.
val pipeline = sendReceive
^
Run Code Online (Sandbox Code Playgroud)
我的代码是:
import scala.util.{Success, Failure}
import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.event.Logging
import akka.io.IO
import spray.can.Http
import spray.client.pipelining._
import spray.util._
import argonaut._, Argonaut._
object test {
case …
Run Code Online (Sandbox Code Playgroud) 我需要从一个case类实例创建一个更新的实例(带有任何需要DecodeJson
的隐式派生),给定一个不完整的json(缺少一些字段).如何通过Argonaut(最好)或Circe(如果必须的话)实现这一目标?
例:
case class Person(name:String, age:Int)
val person = Person("mr complete", 42)
val incompletePersonJson = """{"name":"mr updated"}"""
val updatedPerson = updateCaseClassFromIncompleteJson(person, incompletePersonJson)
println(updatedPerson)
//yields Person(mr updated, 42)
Run Code Online (Sandbox Code Playgroud)
我很确定我必须将json解析为json AST,然后将其转换为Shapeless LabelledGeneric
,然后以某种方式使用Shapeless更新来更新case类实例.
编辑2
在阅读了Shapeless源代码后,我发现我可以生成自己的"默认"对象.我设法创建了一个解决方案,它需要在解析json时出现case类的实例.我希望避免这种情况,而是稍后提供实例.无论如何它是:
import shapeless._
import argonaut._
import ArgonautShapeless._
import shapeless.ops.hlist.Mapper
case class Person(name: String, age: Int)
object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}
def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
g: Generic.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, …
Run Code Online (Sandbox Code Playgroud) 我正在尝试编码/解码以下案例类
case class Person(name: String, age: Int, childs: List[Person])
Run Code Online (Sandbox Code Playgroud)
使用以下代码:
object Person {
implicit def PersonCodecJson =
casecodec3(Person.apply, Person.unapply)("name", "age", "childs")
}
Run Code Online (Sandbox Code Playgroud)
与argonaut,但我得到以下编译器错误:
could not find implicit value for evidence parameter of type argonaut.EncodeJson[List[Person]]
Run Code Online (Sandbox Code Playgroud)
显然,编译器不知道如何处理List [Person]的编码,因为它在如何编码Person的定义中使用.
是否有一种聪明的方式告诉argonaut如何以正确的方式对其进行编码?
更新:感谢Travis:现在正在编译,但它无法正常工作.
implicit def PersonCodecJson : CodecJson[Person] =
casecodec3(Person.apply, Person.unapply)("name", "age", "childs")
Run Code Online (Sandbox Code Playgroud)
导致无限递归和尝试解码的堆栈溢出
val input = """
[{"name": "parent1", "age": 31, "childs": [{"name": "child1", "age": 2, "childs": []}]},
{"name": "parent2", "age": 29, "childs": []}
]
"""
val persons = input.decodeOption[List[Person]].getOrElse(Nil)
Run Code Online (Sandbox Code Playgroud)
结果是
at Person$.PersonCodecJson(main.scala:8) …
Run Code Online (Sandbox Code Playgroud) 我正在使用Scala&Argonaut,尝试解析以下JSON:
[
{
"name": "apple",
"type": "fruit",
"size": 3
},
{
"name": "jam",
"type": "condiment",
"size": 5
},
{
"name": "beef",
"type": "meat",
"size": 1
}
]
Run Code Online (Sandbox Code Playgroud)
而奋力工作,如何进行迭代,把值到一个List[MyType]
地方MyType
都会有名称,类型和大小等属性.
我将很快发布更具体的代码(我尝试了很多东西),但基本上我想了解光标的工作方式,以及如何遍历数组等.我尝试使用\\
(downArray)移动到头部数组,然后:->-
迭代数组,然后--\
(downField)不可用(至少IntelliJ不这么认为).所以问题是我如何:
jdecode[String]
?as[String]
?在Argonaut DecodeJson
特性中,存在一种|||
将解码器链接在一起的方法,从而选择第一个后续解码器.还有一种类似的方法DecodeResult
具有相同的效果.乍一看,好像其中一个是我们想要解码共同特征的多个子类型的东西.但是,我们如何实际做到这一点?
第一个问题是,参数|||
必须是DecodeJson
解码被调用者应该解码的类型的超类型(并且类似地DecodeResult
).我希望这样的解码器能够解码常见超类型的所有子类型,所以这似乎是无限递归的一个秘诀!
asInstanceOf
在定义CodecJson
超类型时,我们可以使用以下丑陋的黑客来解决这个问题:
c => c.as[A] ||| c.as[Foo](implicitly[DecodeJson[B]].asInstanceOf[DecodeResult[Foo]])
Run Code Online (Sandbox Code Playgroud)
但是,当解码两个以上的子类型时仍然存在问题.假设有亚型A
,B
和C
的Foo
.现在怎么样?我们如何为这个解码表达式添加另一种替代方案?.asInstanceOf[DecodeResult[AnyRef]]
将破坏解析结果的类型安全性(好像我们此时尚未丢弃类型安全性!).然后我们很快就会用掉4,5或6种替代方案.
编辑:我很乐意接受使用Argonaut解码超过2宽子类型层次结构的任何替代方法作为答案.
我有一个使用一堆 Java 代码的 Scala 项目,例如这个 Java 源代码:
public enum Category { FOO, BAR };
Run Code Online (Sandbox Code Playgroud)
然后我有一堆 Scala 案例类,我使用 Argonaut 将它们序列化为 JSON 或从 JSON 序列化,如下所示:
case class Thing (a: String, b: Int, c: Float)
object Thing {
implicit val j = casecodec3 (Thing.apply, Thing.unapply)("a", "b", "c")
implicit val e: Equal[Guild] = Equal.equal (_ == _)
}
Run Code Online (Sandbox Code Playgroud)
好的,现在我想编写一个使用 Java 枚举的 Scala 案例类,如下所示:
case class Thing (a: String, b: Int, c: Float, d: Category)
object Thing {
implicit val j = casecodec4 (Thing.apply, Thing.unapply)("a", …
Run Code Online (Sandbox Code Playgroud) 我是Scala的新手,在这里我正在尝试创建一个基于Argonaut的通用json转换器.我试图搜索谷歌和stackoverflow,但到目前为止,我没有任何线索.
这是我的代码片段.
import org.springframework.http.converter.AbstractHttpMessageConverter
import org.springframework.http.{MediaType, HttpInputMessage, HttpOutputMessage}
import scala.io.Source
import argonaut._,Argonaut._
case class Currency(code: String)
object Currency {
implicit def CurrencyCodecJson: CodecJson[Currency] = casecodec1(Currency.apply, Currency.unapply)("code")
}
case class Person(firstName: String, lastName: String)
object Person {
implicit def PersonCodecJson: CodecJson[Person] = casecodec2(Person.apply, Person.unapply)("firstName", "LastName")
}
class ArgonautConverter extends AbstractHttpMessageConverter[Object](new MediaType("application", "json", Charset.forName("UTF-8")), new MediaType("application", "*+json", Charset.forName("UTF-8"))) {
val c = classOf[Currency]
val p = classOf[Person]
def writeInternal(t: Object, outputStream: OutputStream) = {
val jsonString = t match {
case c:Currency …
Run Code Online (Sandbox Code Playgroud) 我很难通过Argonaut文档,所以我想我只想问一个简单的例子.
val input = """{"a":[{"b":4},{"b":5}]}"""
val output = ??? // desired value: List(4, 5)
Run Code Online (Sandbox Code Playgroud)
我可以将光标放到数组中:
Parse.parse(input).map((jObjectPL >=> jsonObjectPL("a") >=> jArrayPL)(_))
// scalaz.\/[String,Option[scalaz.IndexedStore[argonaut.Argonaut.JsonArray,
// argonaut.Argonaut.JsonArray,argonaut.Json]]] =
// \/-(Some(IndexedStoreT((<function1>,List({"b":4}, {"b":5})))))
Run Code Online (Sandbox Code Playgroud)
但那又怎样?我是在正确的轨道上吗?我甚至应该使用游标吗?
编辑 - 我想这是一些进展.我为列表写了一个解码器:
Parse.parse("""[{"b": 4}, {"b": 5}]""")
.map(_.as(IListDecodeJson(DecodeJson(_.downField("b").as[Int]))))
// scalaz.\/[String,argonaut.DecodeResult[scalaz.IList[Int]]] =
// \/-(DecodeResult(\/-([4,5])))
Run Code Online (Sandbox Code Playgroud)
编辑 - 慢慢开始把它放在一起......
Parse.parse(input).map(_.as[HCursor].flatMap(_.downField("a").as(
IListDecodeJson(DecodeJson(_.downField("b").as[Int])))))
// scalaz.\/[String,argonaut.DecodeResult[scalaz.IList[Int]]] =
// \/-(DecodeResult(\/-([4,5])))
Run Code Online (Sandbox Code Playgroud)
编辑 - 所以我想到目前为止我的最佳解决方案是:
Parse.parse(input).map(_.as(
DecodeJson(_.downField("a").as(
IListDecodeJson(DecodeJson(_.downField("b").as[Int])).map(_.toList)
))
))
Run Code Online (Sandbox Code Playgroud)
但是感觉有点冗长.
使用circe
或者argonaut
,我怎么能写一个Json => A
(注释 - Json
可能不是类型的名称)A
由SSN
类给出的位置:
// A USA Social Security Number has exactly 8 digits.
case class SSN(value: Sized[List[Nat], _8])
Run Code Online (Sandbox Code Playgroud)
?
伪代码:
// assuming this function is named f
f(JsArray(JsNumber(1)))
A
因为它的大小是1而不会成为一个
f(JsArray(JsNumber(1), ..., JsNumber(8)))
=== SSN(SizedList(1,...,8))