Jaxb: Как заменить класс / привязку в данном дереве объектов при демаршаллинге - PullRequest
0 голосов
/ 31 августа 2018

У меня есть заданный набор классов для демонтажа xml в дерево объектов. Теперь я получил расширенный XML и хочу заменить один класс в дереве объектов на расширенную версию.

XML-файл

<?xml version="1.0" encoding="UTF-8"?>
<RootNode_001>
    <systemId>on the 4</systemId>
    <systemName>give me some more</systemName>
    <person>
        <firstname>James</firstname>
        <lastname>Brown</lastname>
        <address>
            <street>Funky</street>
            <city>Town</city>
            <type>HOME</type> <!-- this is the new field -->
        </address>
    </person>
</RootNode_001>

Я создаю новый класс адресов с новым полем, например:

public class ExtAddress extends Address {
    // inherit from address and add new field
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

Теперь я пытаюсь распаковать в дерево объектов и ожидаю, что ExtAddress будет частью дерева следующим образом:

public class Runner {

    public static void main ( String argv[] ) throws Exception {
        File file = new File( "basic.xml" );
        Class cls = RootNode.class;

        // create a context with the root node and the replacement class
        JAXBContext jaxbContext = JAXBContext.newInstance( ExtAddress.class, cls );
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        JAXBElement jaxbElement = unmarshaller.unmarshal( new StreamSource( file ), cls );
        RootNode rootNode = (RootNode) jaxbElement.getValue();

        System.out.println( rootNode.getClass().getName() );
        System.out.println( rootNode.getPerson().getClass().getName()  );
        // this returns Address but I want ExtAddress
        System.out.println( rootNode.getPerson().getAddress().getClass().getName()  );
    }
}

Пока я не использую аннотации. Демонстрационное дерево объектов возвращает Address, а не ExtAddress. Если я добавлю аннотацию XmlType, я получу исключение:

@XmlTyp( name = "address" )
ExtAddress extends Address {
  ...
}

Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "address". Use @XmlType.name and @XmlType.namespace to assign different names to them.
    this problem is related to the following location:
        at jaxb.standard.Address
        at jaxb.ExtAddress

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

Я хочу получить набор стандартных классов в библиотеке и иметь возможность изменить дерево объектов позже как расширение при появлении новых полей в xml.

Ответы [ 3 ]

0 голосов
/ 31 августа 2018

Вместо этого вы должны сделать версионирование схемы XML, однако трудно сделать это хорошо. В связанной статье есть pdf , вы должны прочитать ее.

В прошлом я выполнял «Вариант 3 Изменить targetNamespace схемы», таким образом, вы можете сделать следующее:

у вас есть разные targetNamespaces для каждой версии:

targetNamespace="http://www.exampleSchema.com/v1.0"
targetNamespace="http://www.exampleSchema.com/v1.1"
...

и вы можете сгенерировать необходимые классы JAXB для каждого пространства имен в другой группе:

com.example.schema.generated.v10
com.example.schema.generated.v11
...

Отсюда вам решать, как это прочитать:

  • вы можете заключить договор, что файл должен заканчиваться версией схемы: file1_v10.xml, file2_v11.xml
  • вы можете попробовать читать с последними классами JAXB, в случае неудачи, с предыдущим и т. Д.
  • вы можете первый n. строка как текст файла, и проанализируйте targetNamespace и примените правый JAXB
  • или у вас есть другая опция и т. Д. ...

(конечно, после демаршаллинга вы должны обрабатывать каждую версию отдельно, что не обязательно плохо)

OR

вы можете решить, что вы используете только 1 схему, и можно расширить схему только с помощью необязательных (minOccurs = "0") элементов, это ужасно и имеет некоторые ограничения, но иногда этого достаточно.

OR

Или вы можете сделать это по-другому: вы можете использовать другую платформу XML, которая поддерживает управление версиями схемы, например: JMRI или JiBX (Они не нужны в настоящее время или активно разрабатываются, просто хотел показать несколько примеров. Я действительно хотел связать еще один, но, к сожалению, я забыл его название).

0 голосов
/ 03 сентября 2018

Подобная тема обсуждается здесь: Наследование JAXB, не маршальное для подкласса маршалированного класса

С Джексоном 2.8.6 вы можете просто написать что-то вроде

@Test
public void readXmlToSelfDefinedPojo2() throws Exception {

    ObjectMapper mapper = new XmlMapper();
    ExtAdress pojo = mapper.readValue(
                    Thread.currentThread().getContextClassLoader().getResourceAsStream("52109685.xml"),
                    ExtAdress.class);
    System.out.println(toString(pojo));
}

public String toString(Object obj) throws JsonGenerationException, JsonMappingException, IOException{
    StringWriter w = new StringWriter();
    new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true).writeValue(w, obj);
    return w.toString();
}

Будет напечатано

{
  "systemId" : "on the 4",
  "type" : "test"
}

для

<?xml version="1.0" encoding="UTF-8"?>
  <RootNode_001>
<systemId>on the 4</systemId>
<systemName>give me some more</systemName>
<type>test</type>
<person>
    <firstname>James</firstname>
    <lastname>Brown</lastname>
    <address>
        <street>Funky</street>
        <city>Town</city>
        <type>HOME</type> <!-- this is the new field -->
    </address>
</person>
</RootNode_001>

и

@JsonIgnoreProperties(ignoreUnknown = true)
public class ExtAdress extends Adress {
    public String type;
    public ExtAdress() {
    }
}

и

@JsonIgnoreProperties(ignoreUnknown = true)
public class Adress {
    public String systemId;

    public Adress() {
    }
}
0 голосов
/ 31 августа 2018

Изменение xml с помощью extAddress вместо адреса поможет вам.

...