我的任务是通过第三方API集成外部系统。我收到的数据是XML文件,具有给定的结构,而API期望JSON具有不同的结构。解析和生成要么本身不是问题,但转换此类数据的最佳做法是什么。我编写了下面我能想到的解决方案。
我提出的第一个解决方案是分别创建两个模型并创建转换器。这样,映射不是问题。但是,我必须重新创建并维护已存在的域模型。重用是没有选择的,因为这个域模型已有二十年历史,并且不能与业务逻辑分离。此外我觉得我违反了SOC principle,因为转换器需要了解模型的结构。
或者,我可以使用JAXB和Jackson注释创建单个模型并对其进行注释。该方法提供单个模型而无需转换器。这减少了这些类的维护。另一方面,这可能会产生更多胶水代码,以弥补两种模型之间的结构差异。
这些解决方案都没有吸引我。但我愿意接受一个人作为我的命运,如果必须的话。如果还有其他方法可以解决这个问题,我将非常感谢您了解它。关于这两种方法的例子的来源都受到高度赞赏。
答案 0 :(得分:1)
XSLT为这种需求提供了一个优雅的解决方案。例如,这是一个示例转换,它适用于this page上提供的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>
</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>
答案 1 :(得分:1)
解决方案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:正向和反向。否则将很难测试。
我看过几次的替代方案是实际上有两个模型并使用像Dozer这样的东西在它们之间进行转换:
XML (incoming)
-(JAXB)-> Java objects (incoming XML model)
-(Dozer)-> Java object (JSON-based model)
-(Jackson)->
JSON
这也可行。您应该有一个XML Schema用于传入的XML,因此可以生成传入XML模型的Java类,因此这里没有太多的维护开销。问题是(至少对我来说)Dozer和喜欢的东西比XSLT更不灵活和强大。我认为编写XSLT以在XML结构之间进行转换比使用Dozer在Java结构之间进行转换要容易得多。
答案 2 :(得分:-1)
我的建议是,你可以为xml结构创建模型并将模型转换为json。有第三方罐子将模型转换为json,反之亦然。