将不匹配的数据模型从 XML 转换为 JSON 的最佳实践



>问题

我的任务是通过他们的第三方API集成外部系统。我收到的数据是具有给定结构的 XML 文件,而 API 需要具有不同结构的 JSON。解析和生成两者本身都不是问题,而是转换此类数据的最佳实践是什么。我在下面编译了我能想到的解决方案。

解决方案 1

我想出的第一个解决方案是分别创建两个模型并创建一个转换器。这样映射就不那么成问题了。但是,我必须重新创建和维护一个已经存在的域模型。重用是没有选择的,因为此域模型已有 20 年的历史,无法与业务逻辑分开。此外,我觉得我违反了 SOC 原则,因为转换器需要了解模型的结构。

解决方案 2

或者,我可以创建一个模型,并使用 JAXB 和杰克逊注释对其进行注释。这种方法提供了单个模型,无需转换器。这减少了这些类的维护。另一方面,这可能会产生更多的胶水代码来弥补两种模型之间的结构差异。


这些解决方案都没有吸引我。但是,如果有必要的话,我愿意接受一个作为我的命运。如果有任何其他方法可以解决这个问题,我将不胜感激。高度赞赏这两种方法的示例来源。

解决方案2肯定更具吸引力。但问题是你说你的XML和JSON有不同的结构。如果存在结构差异,则必须对其进行补偿。我不确定单一模型怎么可能,补偿必须以某种方式发生。

所以重点是你需要某种灵活的方法来映射不同的结构。XSLT 是一个很好的工具。所以我建议一个不同的解决方案。

只创建一个与您的 JSON 匹配的模型,使用 Jackson 注释(或任何用于 JSON 的注释)对其进行注释。还要用 JAXB 注释对其进行注释。到目前为止,它就像解决方案 2。但是,问题是在这种情况下,XML结构基于JSON结构,并且与传入的XML结构不直接兼容。若要解决此问题,请编写一个 XSLT 转换,该转换将从传入的 XML 转换为基于 JSON 的 XML 结构。本质上:

XML (incoming)  
-(XSLT)-> XML (JSON-based)
-(JAXB)-> Java objects
-(Jackson)->
JSON

XSLT 是非常强大和灵活的 XML 转换工具。一个重要的缺点是,您可能需要两个 XSLT:正向和反向。否则测试起来会相当困难。

我见过几次的另一种方法是实际上有两个模型并使用类似推土机的东西在它们之间进行转换:

XML (incoming)  
-(JAXB)-> Java objects (incoming XML model)
-(Dozer)-> Java object (JSON-based model)
-(Jackson)->
JSON

这也可能有效。您应该为传入的 XML 提供一个 XML 模式,以便可以生成传入 XML 模型的 Java 类,因此您在这里没有太多的维护开销。问题是(至少对于我的措施而言)推土机和类似的东西远不如 XSLT 灵活和强大。我认为编写 XSLT 在 XML 结构之间进行转换比使用 Dozer 在 Java 结构之间进行转换要容易得多。

XSLT 为这种需求提供了一个优雅的解决方案。例如,下面是一个示例转换,它适用于此页面上提供的 XML,并将其转换为同一页面上给定的 json。

<?xml version="1.0" encoding="utf-8"?>    
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" />
<xsl:strip-space elements="*" />
<xsl:variable name="nl"><xsl:text>&#xa;</xsl:text></xsl:variable>
<xsl:variable name="q">"</xsl:variable>
<xsl:variable name="bInd" select="'  '" />
<xsl:template match="glossary">
<xsl:value-of select="concat('{', $nl, $bInd, $q, local-name(.), $q, ' : {')" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($bInd, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $bInd, '}', $nl, '}', $nl)" />
</xsl:template>
<xsl:template match="title">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, '  ', $q, local-name(.), $q, ' : ', $q, ., $q)" />
</xsl:template>
<!-- Other text elements -->
<xsl:template match="*">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : ', $q, ., $q)" />
</xsl:template>
<xsl:template match="GlossEntry">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, $q, local-name(.), $q, ' : {')" />
<xsl:value-of select="concat($nl, $ind, $bInd, '  ',$q, 'ID', $q, ' : ', $q, @ID , $q)" />
<xsl:value-of select="concat($nl, $ind, $bInd, ', ', $q, 'SortAs', $q, ' : ', $q, @SortAs , $q)" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($ind, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
<xsl:template match="GlossDef">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : {')" />
<xsl:value-of select="concat($nl, $ind, $bInd, '  ', $q, 'para', $q, ' : ', $q, para, $q)" />
<xsl:value-of select="concat($nl, $ind, $bInd, ', ', $q, 'GlossSeeAlso', $q, ' : [ ')" />
<xsl:for-each select="GlossSeeAlso">
<xsl:apply-templates select=".">
<xsl:with-param name="pos" select="position()" />
</xsl:apply-templates>
</xsl:for-each>
<xsl:value-of select="' ]'" />
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
<xsl:template match="GlossSeeAlso">
<xsl:param name="pos" />
<xsl:if test="$pos > 1">
<xsl:value-of select="', '" />
</xsl:if>
<xsl:value-of select="concat($q, @OtherTerm, $q)" />
</xsl:template>
<xsl:template match="GlossSee">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : ', $q, @OtherTerm, $q)" />
</xsl:template>
<xsl:template match="GlossDiv | GlossList">
<xsl:param name="ind" />
<xsl:value-of select="concat($nl, $ind, ', ', $q, local-name(.), $q, ' : {')" />
<xsl:apply-templates>
<xsl:with-param name="ind" select="concat($ind, $bInd)" />
</xsl:apply-templates>
<xsl:value-of select="concat($nl, $ind, '}')" />
</xsl:template>
</xsl:transform>

此示例转换是 XSLT 1.0。可以通过在 XML 文件中添加对它的引用并在 Web 浏览器中加载 XML 文件来测试它。

例如,如果上述转换存储在 test.xsl 中,请将 XML 放在 test 中.xml并按如下所示引用转换:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<glossary>
<title>example glossary</title>
<GlossDiv>
<title>S</title>
<GlossList>
<GlossEntry ID="SGML" SortAs="SGML">
<GlossTerm>Standard Generalized Markup Language</GlossTerm>
<Acronym>SGML</Acronym>
<Abbrev>ISO 8879:1986</Abbrev>
<GlossDef>
<para>A meta-markup language, used to create markup languages such as DocBook.</para>
<GlossSeeAlso OtherTerm="GML" />
<GlossSeeAlso OtherTerm="XML" />
</GlossDef>
<GlossSee OtherTerm="markup" />
</GlossEntry>
</GlossList>
</GlossDiv>
</glossary>

我的建议是,您可以为 xml 结构创建模型并将模型转换为 json。 有第三方 jar 会将模型转换为 JSON,反之亦然。

另请查看此链接 - 在 Java 中将 XML 转换为 JSON 的最快方法

最新更新