1

我正在构建一个调用 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来概括我们的构造和每种包的具体案例类。

因此,识别包类型的唯一方法是通过idsubId,两者都存在于包元数据中。为了构建我们的结构,我们为每个返回该类型的具体类型创建了一个函数(函数采用泛型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 中,但我无法弄清楚如何改变使用它的方法。

提前致谢!

4

0 回答 0