多种类型的通用JSON序列化

时间:2015-11-10 01:01:15

标签: json scala playframework play-json

我正在构建一个调用SOAP服务的Play API。这是我们的提供商给我们的唯一沟通机制。该服务返回“Packages”的列表,如下所示:

<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将返回一个包含所有Packages限定的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

我们知道大致有六种或七种类型。为了清楚起见,我包括三个。请注意,某些类型只有“选项”,有些只有“类型”,两个特定情况都有。 我们使用特征Package来概括我们的构造和每种类型的包的具体案例类。

因此,识别包类型的唯一方法是通过idsubId,两者都存在于包元数据中。为了构建我们的结构,我们为每个返回该类型的具体类型创建了一个函数(使用泛型PackageDTO的函数返回一个Package1对象,函数使用泛型PackageDTO返回一个{{ 1}} object等等,返回我们的函数并评估条件以确定Package的类型:

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)) 。我们真正的问题从这里开始......

问题

我们为我们的类型提供了所有JSON读者/写作者:List[Package]PackageMetadataPackageType,但是,因为我们无法为特征编写读者/编写者({{ 1}})我们无法将PackageOption转换为我们想要的JSON结构。

问题

是否有任何“通用”方式将我们的Package转换为JSON结构,我们可以在其中了解每种类型的包?让(Package,Package,...)得到(Package1,Package2,...)JSON结构,就像我们之前指定的那样。

我认为在Shapeless和HList中我无法弄清楚如何改变使用它的方法。

提前致谢!

0 个答案:

没有答案