java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JAXB解析xml转换成类

JAXB解析xml转换成类的实现方式

作者:码上走人

本文主要介绍了如何使用JAXB将XML配置项转换为Java类,JAXB提供了多种注解,如@XmlRootElement、@XmlElement、@XmlElementWrapper、@XmlAttribute等,可以方便地将XML元素映射为Java对象,并且可以控制生成的XML结构,同时,文章也提到了一些需要注意的问题

项目需要将一些公共配置项写成xml文件,启动时转化成对应的类。

查找了一些资料,发现JAXB完全可以满足自己的功能需求。

pom依赖

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.1</version>
</dependency>

主要注解

@XmlRootElement

@XmlRootElement定义xml根节点的信息。主要功能如下:

1. 将元素与XML模式类型相关联

@XmlRootElement
      class Point {
         int x;
         int y;
         Point(int _x,int _y) {x=_x;y=_y;}
      }
  
//Example: Code fragment corresponding to XML output
marshal( new Point(3,5), System.out);
 <!-- Example: XML output -->  
 <point>    
    <x> 3 </x>
    <y> 5 </y>
</point>


//注释在模式中生成全局元素声明。全局元素声明与类映射到的XML模式类型相关联。

<!-- Example: XML schema definition -->
<xs:element name="point" type="point"/>
<xs:complexType name="point">
       <xs:sequence>
                        <xs:element name="x" type="xs:int"/>
                        <xs:element name="y" type="xs:int"/>
       </xs:sequence>
</xs:complexType>

2. 将全局元素与类所属的XML模式类型相关联

@XmlRootElement(name="PriceElement")
public class USPrice {
    @XmlElement
    public java.math.BigDecimal price;
}


Example: XML schema definition -->   
 <xs:element name="PriceElement" type="USPrice"/>   
<xs:complexType name="USPrice">     
    <xs:sequence>       
    <xs:element name="price" type="xs:decimal"/>     
    </sequence>
</xs:complexType>

@XmlElement

@XmlElement可以将xml元素转为对应的实体属性值。可转化普通属性、数组、对象

1. 将公共非静态非最终字段映射到本地元素

public class USPrice {
  @XmlElement(name="itemprice")
  public java.math.BigDecimal price;
}

<!-- Example: Local XML Schema element -->   
<xs:complexType name="USPrice"/>     
  <xs:sequence>       
    <xs:element name="itemprice" type="xs:decimal" minOccurs="0"/>     
  </sequence>   
</xs:complexType>

2.  映射到可nullable元素。

public class USPrice {
    @XmlElement(nillable=true)
    public java.math.BigDecimal price;
}

<!-- Example: Local XML Schema element -->   
<xs:complexType name="USPrice">     
    <xs:sequence>       
        <xs:element name="price" type="xs:decimal" nillable="true" minOccurs="0"/>     
    </sequence>   
</xs:complexType>

3. 映射到可nullable元素且必填的元素。 

public class USPrice {
    @XmlElement(nillable=true, required=true)
    public java.math.BigDecimal price;
}

<!-- Example: Local XML Schema element -->   
<xs:complexType name="USPrice">     
    <xs:sequence>       
        <xs:element name="price" type="xs:decimal" nillable="true" minOccurs="1"/>     
    </sequence>   
</xs:complexType>

@XmlElements

@XmlElements可以将不同的xml节点转换为实体数组对象。

public class Foo {
     XmlElements(
         XmlElement(name="A", type=Integer.class),
         XmlElement(name="B", type=Float.class)
      )
      public List items;
 }

// items = [1,2.5]时,得到的xml为
 <A> 1 </A>
 <B> 2.5 </B>
 ...

 <!-- XML Schema fragment -->
 <xs:complexType name="Foo">
   <xs:sequence>
     <xs:choice minOccurs="0" maxOccurs="unbounded">
       <xs:element name="A" type="xs:int"/>
       <xs:element name="B" type="xs:float"/>
     <xs:choice>
   </xs:sequence>
 </xs:complexType>

映射到用另一个元素包裹的元素列表

 // Mapped code fragment
  public class Foo {
      XmlElementWrapper(name="bar")
      XmlElements(
          XmlElement(name="A", type=Integer.class),
          XmlElement(name="B", type=Float.class)
      }
      public List items;
  }
映射结果
  <!-- XML Schema fragment -->
  <xs:complexType name="Foo">
    <xs:sequence>
      <xs:element name="bar">
        <xs:complexType>
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="A" type="xs:int"/>
            <xs:element name="B" type="xs:float"/>
          </xs:choice>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

使用适配器根据类型更改元素名称 

class Foo {
   XmlJavaTypeAdapter(QtoPAdapter.class)
   XmlElements({
       XmlElement(name="A",type=PX.class),
       XmlElement(name="B",type=PY.class)
   })
   Q bar;
}

XmlType abstract class P {...}
XmlType(name="PX") class PX extends P {...}
XmlType(name="PY") class PY extends P {...}

<!-- XML Schema fragment -->
<xs:complexType name="Foo">
  <xs:sequence>
    <xs:element name="bar">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="A" type="PX"/>
          <xs:element name="B" type="PY"/>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>

@XmlElementWrapper

围绕XML表示生成包装器元素。

这主要是为了在集合周围生成一个包装器XML元素。

通过这个 @XmlElementWrapper注解可以减少包装类的创建。

 //Example: code fragment    
 int[] names;    
 
 // XML Serialization Form 1 (Unwrapped collection)  
 <names> ... </names>  
 <names> ... </names>    
 
 // XML Serialization Form 2 ( Wrapped collection )  
 <wrapperElement>     
    <names> value-of-item </names>     
    <names> value-of-item </names>     
    ....  
</wrapperElement>

@XmlElementWrapper 需要配合 @XmlElement 一起使用

@XmlList

@XmlList注释允许在单个元素中将多个值表示为空格分隔的标记

@XmlRootElement
  class Foo {
      @XmlElement
      @XmlList
      List<String> data;
  }
  
the above code will produce XML like this:
 <foo>  

         <data>abc def</data>

 </foo>

@XmlAttribute

@XmlAttribute可以获取到xml节点的属性字段值 

@XmlElementRef

该注解所在属性是某些子类的父类,起到引用的作用。

同时标注得有@XmlElementRef的类属性,其子类上需要使用@XmlRootElement标注,否则转换异常,提示找不到具体的引用实现。

另外,转换时,需要将其子类的class一起传递到JAXBContext上下文中,否则也无法转换。

@XmlTransient

通常与 @XmlElement 须搭配使用的。

@XmlElement用在属性上,用于指定生成xml的节点名,@XmlTransient用在对应的getter方法上,起到关联的作用。

@XmlAccessorOrder

控制JAXB 绑定类中属性和字段的排序。

其他

@XmlValue、@XmlEnum、@XmlEnumValue

实现

定义xml信息

<person>
    <id>111111</id>
    <name>lijie</name>
    <address>
        <province></province>
        <city></city>
        <detail></detail>
    </address>
    <attr property="1231321" />
    <items>
        <item>afgafdafd</item>
        <item>adfadfadfa</item>
    </items>
</person>

定义类

这里通过使用@XmlElementWrapper注解配合可以减少包装类的创建

@XmlRootElement(name = "person")
public class Person{
    
    @XmlElement(name = "id")
    String id;

    @XmlElement(name = "name")
    String name;

    @XmlElement(name = "address")
    Address addrss;

    @XmlElement(name = "attr")
    Attributes attributes;
    
    @XmlElementWrapper(name =  "items")
    @XmlElement(name = "item")
    List<Item> items;
}

public class Address{
    
    @XmlElement(name = "province")
    String province;

    @XmlElement(name = "city")
    String city;
}

public class Item{
    
    @XmlElement(name = "item")
    String des;
}


public class Attributes{
    
    @XmlAttribute(name = "property")
    String des;
}

转换

xml转换为实体类

ClassPathResource resource = new ClassPathResource("xml/SnmpTrapHitRules.xml");
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getStream()));

JAXBContext jaxbContext = JAXBContext.newInstance(SnmpTrapHitRuleDto.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
snmpTrapHitRuleDto = (SnmpTrapHitRuleDto) unmarshaller.unmarshal(reader);

实体类转成xml

 // 创建Person对象
Person person = new Person("张三", 30);

// 创建JAXBContext对象
JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);

// 创建Marshaller对象
Marshaller marshaller = jaxbContext.createMarshaller();

// 设置格式化输出
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

// 将Java对象转换为XML字符串
StringWriter stringWriter = new StringWriter();
marshaller.marshal(person, stringWriter);

问题

JAXB要求将注解放到对应的set/get方法上,但是由于我们可能使用的时lombok,没有显示的说明get/set方法,这时候会报错:类的两个属性具有相同名称。

解决办法是在类上加入注解 @XmlAccessorType

类级别的注解。定义这个类中的何种类型需要映射到XML。

解释起来有点拗口,可以通过它的属性值更好理解这个参数的意义。

参数 value 可以接受4个指定值,这几个值是枚举类型,方便调用:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文