Рекомендации по преобразованию несовпадающих моделей данных из xml в json

Проблема

Мне поручено интегрировать внешнюю систему через их сторонний API. Получаемые мной данные представляют собой файлы XML с заданной структурой, тогда как API ожидает JSON с другой структурой. Анализ и генерация не являются проблемой сами по себе, но каковы наилучшие методы преобразования таких данных. Я собрал решения, которые могу придумать ниже.

Решение 1

Первым решением, которое я придумал, было создание обеих моделей по отдельности и создание конвертера. Таким образом, отображение менее проблематично. Однако мне нужно воссоздать и поддерживать уже существующую модель предметной области. Повторное использование невозможно, потому что этой модели предметной области уже двадцать лет, и она неотделима от бизнес-логики. Кроме того, я чувствую, что нарушаю Принцип SOC из-за того, что конвертеры должны знать о структуре моделей.

Решение 2

В качестве альтернативы я могу создать одну модель и аннотировать ее с помощью аннотаций JAXB и Джексона. Такой подход позволяет получить единую модель без преобразователя. Это снижает потребность в обслуживании этих классов. С другой стороны, это, вероятно, даст больше связующего кода, чтобы компенсировать структурные различия между обеими моделями.


Мне ни одно из этих решений не нравится. Но я готов принять одну как свою судьбу, если придется. Если есть другой способ решения этой проблемы, я был бы признателен, если бы узнал об этом. Источники с примерами для любого подхода приветствуются.

Как возможно Решение 2, если XML и JSON имеют разные структуры?

lexicore 11.04.2018 20:15

Существуют способы извлечения вложенных структур XML, например, с помощью XPATH, которые отображают различные структуры в одну модель.

tgr 12.04.2018 07:13
1
2
390
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я предлагаю создать модель для структуры xml и преобразовать модель в json. есть сторонние jar-файлы, которые конвертируют модель в json и наоборот.

Также проверьте эту ссылку - Самый быстрый способ конвертировать XML в JSON на Java

Это почему? Что отличает это решение от решения с единственной моделью?

tgr 11.04.2018 12:14

По сути, это «Решение 2», если не обращать внимания на то, что XML и JSON имеют разные структуры.

lexicore 11.04.2018 20:31

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-файл и загрузив 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» в «XML, соответствующий структуре JSON»?

lexicore 11.04.2018 20:34
Ответ принят как подходящий

Решение 2 определенно более привлекателен. Но проблема в том, что вы говорите, что ваш XML и JSON имеют разные структуры. Если есть конструктивные отличия, придется их компенсировать. Я не уверен, как это возможно с единственной моделью, компенсация должна как-то происходить.

Итак, все дело в том, что вам нужен какой-то гибкий подход для сопоставления между различными структурами. XSLT - хороший инструмент для этого. Поэтому я бы предложил другое решение.

Создайте только одну модель, соответствующую вашему JSON, аннотируйте ее аннотациями Джексона (или чем угодно, что вы используете для JSON). Также аннотируйте его аннотациями JAXB. Пока это похоже на Решение 2. Проблема, однако, в том, что структура XML в этом случае основана на структуре JSON и не совместима напрямую с входящей структурой XML. Чтобы решить эту проблему, напишите преобразование XSLT, которое преобразует входящий XML в структуру XML на основе JSON. По сути:

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, чтобы можно было сгенерировать классы Java для модели входящего XML, поэтому у вас не будет больших накладных расходов на обслуживание. Проблема в том, что (по крайней мере, по моим меркам) Dozer и лайки намного менее гибкие и мощные, чем XSLT. Я думаю, что намного проще написать XSLT для преобразования между структурами XML, чем использовать Dozer для преобразования между структурами Java.

Другие вопросы по теме