如何阅读带有 java 注释的 xsi:type
How to read xsi:type with java annotations
我想将基于 jaxb 的 xml-file 读入我的面向对象结构。
假设这是我的 xml-file:
<?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 元素的包装器元素。 child可以是xsi:type指定的男孩或女孩。这两个 classes 有一些共同的元素(如 age)和一些不同的(不包括)元素(如 isdancing 或 是足球运动员)
要读取文件,我有这个方法:
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());
}
我的 Children class 看起来像这样:
@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);
}
}
我的 Child class 看起来像这样:
@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 class 看起来像这样:
@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>
我现在的问题是,输出正常,但是 Child-class 的元素 xsiType 始终为 null,否则它会以 IllegalAnnotationExceptions 结束,这与 XmlTest.model.Child.xsiType
所以我预计设置任何类型的@Xml-Annotation 都会出错。有人可以帮我找出错误吗?
目标是迭代 children 的列表并在运行时(基于 xsiType)决定这是女孩还是男孩。
谢谢
您不需要 XsiType
class。
您可以只使用 String
代替。
在你的Child
class
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;
xsi类型通常用于表示对具体类型的引用。 Jaxb 可以使用 xsi 类型而无需进一步的解决方法。
创建 Boy
和扩展 Children
的 Girl
class。 (您可能需要使用 @XmlType
调整类型名称)。这样,所有带有 xsi:type=Girl
的元素都将绑定到 class 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 {
}
}
第二种方法的干净解决方案(基于单独的 class 文件):
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]
我想将基于 jaxb 的 xml-file 读入我的面向对象结构。
假设这是我的 xml-file:
<?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 元素的包装器元素。 child可以是xsi:type指定的男孩或女孩。这两个 classes 有一些共同的元素(如 age)和一些不同的(不包括)元素(如 isdancing 或 是足球运动员)
要读取文件,我有这个方法:
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());
}
我的 Children class 看起来像这样:
@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);
}
}
我的 Child class 看起来像这样:
@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 class 看起来像这样:
@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>
我现在的问题是,输出正常,但是 Child-class 的元素 xsiType 始终为 null,否则它会以 IllegalAnnotationExceptions 结束,这与 XmlTest.model.Child.xsiType
所以我预计设置任何类型的@Xml-Annotation 都会出错。有人可以帮我找出错误吗?
目标是迭代 children 的列表并在运行时(基于 xsiType)决定这是女孩还是男孩。
谢谢
您不需要 XsiType
class。
您可以只使用 String
代替。
在你的Child
class
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;
xsi类型通常用于表示对具体类型的引用。 Jaxb 可以使用 xsi 类型而无需进一步的解决方法。
创建 Boy
和扩展 Children
的 Girl
class。 (您可能需要使用 @XmlType
调整类型名称)。这样,所有带有 xsi:type=Girl
的元素都将绑定到 class 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 {
}
}
第二种方法的干净解决方案(基于单独的 class 文件):
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]