【问题标题】:Best practices for converting non matching datamodels from XML to JSON将不匹配的数据模型从 XML 转换为 JSON 的最佳实践
【发布时间】:2018-04-11 09:50:47
【问题描述】:

问题

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

解决方案 1

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

解决方案 2

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


这些解决方案都不吸引我。但如果必须的话,我愿意接受一个作为我的命运。如果还有其他方法可以解决此问题,我将不胜感激。非常感谢任何一种方法的示例来源。

【问题讨论】:

  • 如果 XML 和 JSON 具有不同的结构,解决方案 2 如何可能?
  • 例如,有一些方法可以使用 XPATH 提取嵌套的 XML 结构,这会将不同的结构映射到单个模型。

标签: java json xml jackson jaxb


【解决方案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 的 XML 模式,以便可以生成传入 XML 模型的 Java 类,因此您在这里没有太多的维护开销。问题是(至少就我的测量而言)Dozer 和 like 远不如 XSLT 灵活和强大。我认为编写 XSLT 在 XML 结构之间转换比使用 Dozer 在 Java 结构之间转换要容易得多。

【讨论】:

    【解决方案2】:

    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>&#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>
    

    【讨论】:

    • 我喜欢这个方向,但我不会这样做。这个 XSLT 同时做两件事:转换 XML 的结构并将 XML 转换为 JSON。这将很难实施和维护。而且也没有必要同时做这两件事。如果 XML 匹配 JSON 结构,它可以自动转换为 JSON。那么为什么不使用 XSLT 将“传入的 XML”“简单”转换为“匹配 JSON 结构的 XML”呢?
    【解决方案3】:

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

    还可以查看此链接 - Quickest way to convert XML to JSON in Java

    【讨论】:

    • 这是为什么呢?是什么让这个解决方案优于单一模型的解决方案?
    • 如果您忽略 XML 和 JSON 具有不同的结构,这基本上是“解决方案 2”。
    猜你喜欢
    • 2014-11-27
    • 2020-01-14
    • 2019-03-31
    • 1970-01-01
    • 2012-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多