我正在构建一个调用 SOAP 服务的 Play API。这是我们的提供商提供给我们的唯一通信机制。该服务返回一个“包”列表,如下所示:
<packages>
<package>
<!-- Cardinality:
"metadata" : 1 (required),
"types" : 0-n (zero or more occurences),
"options" : 0-n (zero or more occurences)-->
<metadata>
<id>1</id>
<subid>1</subid>
<name>Package 1</name>
</metadata>
<types>
...
</types>
<options>
...
</options>
</package>
<package>
...
</package>
...
</packages>
该服务返回包的通用结构,但是,对于我们的 API 使用者,该方法不起作用,因此,我们的 API 将返回一个包含所有合格包的 JSON,如下所示:
{
"packages" : {
"package1" : {
"metadata" : {
"id" : "1",
"subid" : "1"
},
"types" : [
{
...
},
...
],
"options" : [
{
...
},
...
]
},
...
}
}
因此,在 Play 中,我们这样定义了我们的模型:
trait Package
trait PackageTypeA extends Package{
def metadata: PackageMetadata
def types: List[PackageType]
def options: List[PackageOption]
}
trait PackageTypeB extends Package{
def metadata: PackageMetadata
def options: List[PackageOption]
}
trait PackageTypeC extends Package{
def metadata: PackageMetadata
def types: List[PackageType]
}
sealed case class Package1 (metadata: PackageMetadata, types: List[PackageType], options: List[PackageOption]) extends PackageTypeA
sealed case class Package2 (metadata: PackageMetadata, options: List[PackageOption]) extends PackageTypeB
sealed case class Package3 (metadata: PackageMetadata, types: List[PackageType]) extends PackageTypeC
我们知道我们大致有六七种类型。为了清楚起见,我包括三个。请注意,其中一些类型只有“选项”,有些只有“类型”,两个特定情况都有它们。我们使用 traitPackage
来概括我们的构造和每种包的具体案例类。
因此,识别包类型的唯一方法是通过id
和subId
,两者都存在于包元数据中。为了构建我们的结构,我们为每个返回该类型的具体类型创建了一个函数(函数采用泛型PackageDTO
返回一个Package1
对象,函数采用泛型PackageDTO
返回一个Package2
对象等等),返回我们的函数并评估条件以确定类型包装:
//PackageDTO is the structure returned by the SOAP Service.
private def fPackage1(p: PackageDTO): Package1 = {
val metadata: PackageMetadata = buildMetadata(c)
val types: List[PackageType] = p.getPackages.toList map {
package =>
buildBackage(package)
}
val options: List[PackageOption] = c.getOptions.toList.map{
opt =>
buildOption(opt)
}
Package1(metadata, types, options)
}
private def fPackage2(p: PackageDTO): Package2 = {
val metadata: PackageMetadata = buildMetadata(c)
val types: List[PackageType] = p.getPackages.toList map {
package =>
buildBackage(package)
}
Package2(metadata, types)
}
def f[A >: Package](p: PackageDTO): PackageDTO => A = {
if ("1".equals(p.getMetadata.getId) && "1".equals(p.getMetadata.getSubId)) fPackage1
else if ("2".equals(p.getMetadata.getId) && "2".equals(p.getMetadata.getSubId)) fPackage2
else fPackageN
}
def transform[A >: Package](p: PackageDTO)(f: PackageDTO => A): A = {
f(p)
}
所以,我们这样调用我们的转换函数:
transform(c)(f(c))
我们得到(对于所有包)一个List[Package]
. 我们真正的问题从这里开始......
问题
我们的类型都有所有 JSON 读取器/写入器:PackageMetadata
, PackageType
, PackageOption
, 但是,由于我们无法为 Trait ( Package
) 编写读取器/写入器,因此我们无法将我们的类型转换List[Package]
为所需的 JSON 结构。
问题
是否有任何“通用”方法可以将我们List[Package]
转换为 JSON 结构,在其中我们可以知道每种类型的包?让 (Package, Package, ...) 在我们之前指定的 JSON 结构中获取 (Package1, Package2, ...)。
我认为在 Shapeless 和 HList 中,但我无法弄清楚如何改变使用它的方法。
提前致谢!