Как читать xsi: type с аннотациями Java - PullRequest
2 голосов
/ 03 октября 2019

Я хочу прочитать в xml-файле, основанном на jaxb, свою структуру с указанием объекта.

Допустим, это мой xml-файл:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <children xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <child xsi:type="girl">
            <age>12</age>
            <isdancing>true</isdancing>
        </child>
        <child xsi:type="boy">
            <age>10</age>
            <issoccerplayer>true</issoccerplayer>
        </child>
    </children>

children - это некоторый элемент-обертка, включающий несколько дочерних элементов. child может быть либо мальчиком, либо девочкой, указанной в xsi: type. Эти два класса имеют некоторые общие элементы (например, age ) и некоторые различные (исключая) элементы (например, isdancing или issoccerplayer )

Toпрочитайте файл, у меня есть этот метод:

    public static void main( String[] args ) throws JAXBException
    {
        JAXBContext jaxbContext;
        jaxbContext = JAXBContext.newInstance(Children.class);             
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        File file = new File("C:/test.xml");
        if (!file.exists()) System.out.println("File does not exist");

        Children children = (Children) jaxbUnmarshaller.unmarshal(file);
        System.out.println(children.toString());
    }

Мой класс детей выглядит следующим образом:

    @XmlRootElement(name="children")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Children {

        @XmlElement(name="child")
        private List<Child> childrenList;

        public List<Child> getChildren() { return childrenList; }
        public void setChildren(List<Child> children) {this.childrenList = children;}

    @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

Мой класс ребенка выглядит следующим образом:

    @XmlAccessorType(XmlAccessType.FIELD)
    public class Child {

    @XmlAttribute(name="xsi:type")
    private XsiType xsiType;

    private int age;

    @XmlElement(name = "isdancing")
    private boolean isDancing;

    @XmlElement(name = "issoccerplayer")
    private boolean isSoccerPlayer;

        //Getter and setter for all fields

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this);
        }
    }

И мой класс XsiType выглядит следующим образом:

    @XmlAccessorType(XmlAccessType.FIELD)
    public class XsiType {

        @XmlAttribute(name="xsi:type")
        private String name;

        @XmlValue
        private String value;

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getValue() { return value; 
        public void setValue(String value) { this.value = value; }
    }

В моем файле pom.xml я включил следующие зависимости:

    <dependencies>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
    </dependencies>

Моя проблема в том, что вывод в порядке, ноэлемент xsiType класса Child всегда имеет значение null или в противном случае он заканчивается в IllegalAnnotationExceptions, которые связаны с XmlTest.model.Child.xsiType

Так что я ожидаю, что при установке любого вида @ Xml- возникает ошибкаАннотация. Может ли кто-нибудь помочь мне, обнаружив ошибку?

Цель состоит в том, чтобы перебрать список детей и решить во время выполнения (на основе xsiType), является ли это девочка или мальчик.

Спасибо

Ответы [ 3 ]

2 голосов
/ 03 октября 2019

Вам не нужен ваш XsiType класс. Вместо этого вы можете просто использовать String.

В вашем классе Child атрибут xsiType должен выглядеть следующим образом.

@XmlAttribute(name = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
private String xsiType;

Примечание: в аннотации @XmlAttribute

  • используйте name = "type" (без префикса xsi:)
  • укажите параметр namespace, указанный в вашем XML для xmlns:xsi="..."

Кстати:
Вместо ввода строки "http://www.w3.org/2001/XMLSchema-instance" лучше использовать константу XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI. Ваш улучшенный код будет выглядеть так:

@XmlAttribute(name = "type", namespace = XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)
private String xsiType;
1 голос
/ 04 октября 2019
Тип

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

Создайте классы Boy и Girl, которые расширяют Children. (Возможно, вам придется настроить имена типов с помощью @XmlType). При этом все элементы с xsi:type=Girl будут связаны с классом Girl

@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({ Boy.class, Girl.class }) // Either use @XmlSeeAlso to register classes in the JaxbContext
                                       //  or add them to the context directly
public class Child {

    private int age;

    @XmlElement(name = "isdancing")
    private boolean isDancing;

    @XmlElement(name = "issoccerplayer")
    private boolean isSoccerPlayer;

    // Getter and setter for all fields

}

@XmlType(name = "boy") // can be omitted if default value matches with the default value
public class Boy extends Child {

}

@XmlType(name = "girl")
public class Girl extends Child {

}

Полный автономный пример:

package jaxb;

import java.io.File;
import java.io.StringReader;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;

public class Inheritance {

    public static void main(String[] args) throws JAXBException {
        JAXBContext jaxbContext;
        jaxbContext = JAXBContext.newInstance(Children.class);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

        String x = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n"
                + "    <children xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
                + "        <child xsi:type=\"girl\">\r\n" + "            <age>12</age>\r\n"
                + "            <isdancing>true</isdancing>\r\n" + "        </child>\r\n"
                + "        <child xsi:type=\"boy\">\r\n" + "            <age>10</age>\r\n"
                + "            <issoccerplayer>true</issoccerplayer>\r\n" + "        </child>\r\n" + "    </children>";

        Children children = (Children) jaxbUnmarshaller.unmarshal(new StringReader(x));
        System.out.println(children.getChildren().toString());
    }

    @XmlRootElement(name = "children")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Children {

        @XmlElement(name = "child")
        private List<Child> childrenList;

        public List<Child> getChildren() {
            return childrenList;
        }

        public void setChildren(List<Child> children) {
            this.childrenList = children;
        }

    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlSeeAlso({ Boy.class, Girl.class })
    public static class Child {

        private int age;

        @XmlElement(name = "isdancing")
        private boolean isDancing;

        @XmlElement(name = "issoccerplayer")
        private boolean isSoccerPlayer;

        // Getter and setter for all fields

    }

    @XmlType(name = "boy")
    public static class Boy extends Child {

    }

    @XmlType(name = "girl")
    public static class Girl extends Child {

    }
}
0 голосов
/ 04 октября 2019

Чистое решение для второго подхода (на основе отдельных файлов классов):

public class App
{
    public static void main(String[] args) throws JAXBException
    {
        JAXBContext jaxbContext = JAXBContext.newInstance(Children.class);             
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        File file = new File("C:/test2.xml");
        Children children = (Children) jaxbUnmarshaller.unmarshal(file);

        for (Child c : children.getChildren()) {
            if (c instanceof Boy) {
                System.out.println(((Boy)c).toString());
            } else if (c instanceof Girl){
                System.out.println(((Girl)c).toString());
            }
        }
    }
}

Children.java

@XmlRootElement(name="children")
@XmlAccessorType(XmlAccessType.FIELD)
public class Children {

    @XmlElement(name="child")
    private List<Child> childrenList;

    public List<Child> getChildren() { return childrenList; }
    public void setChildren(List<Child> children) {this.childrenList = children;}

    @Override
    public String toString() { return ReflectionToStringBuilder.toString(this); }
}

Boy.java

@XmlType(name="boy")
public class Boy extends Child {

    @XmlElement(name = "issoccerplayer")
    private boolean isSoccerPlayer;

    public boolean isSoccerPlayer() { return isSoccerPlayer; }
    public void setSoccerPlayer(boolean isSoccerPlayer) { this.isSoccerPlayer = isSoccerPlayer; }

    @Override
    public String toString() { return ReflectionToStringBuilder.toString(this); }
}

Girl.java

@XmlType(name="girl")
public class Girl extends Child {

    @XmlElement(name = "isdancing")
    private boolean isDancing;

    public boolean isDancing() { return isDancing; }
    public void setDancing(boolean isDancing) { this.isDancing = isDancing; }

    @Override
    public String toString() { return ReflectionToStringBuilder.toString(this); }
}

Child.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({ Boy.class, Girl.class }) 
public abstract class Child {

    private int age;

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

Выходные данные должны быть:

de.home.myproject.XmlTest.model.Girl@12edcd21[isDancing=true,age=12]
de.home.myproject.XmlTest.model.Boy@27bc2616[isSoccerPlayer=true,age=10]
...