根据昨天的DOM 补充一个jaxb 解析XML的说明

昨天总结了一个关于dom解析xml的东西
今天作为昨天内容的一个补充吧,昨天那个点下面连接
java jdk 自带 DOM 解析xml 笔记

xml 转java object

今天补充一个jaxb的xml解析方法。
这个jaxb也是jdk自带的解析xml方法,他的方式与dom不同。dom是直接将xml解析成dom 树进行循环迭代进行读取,添加操作。而jaxb是 直接将xml转成相对应的数据对象。先写一个简单的例子
一个简单的数据对象类User

@XmlRootElement(name = "User")
public class User {
	String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [name=" + name + "]";
	}
}

还有一个对应的xml

<User><name>sss</name></User>

以及一个JAXB处理xml的方法 ,详细说明看样例注释吧

// 解析的xml声明
	static String xml="<User><name>sss</name></User>";
	
	public static void main(String a[])throws Exception {
		// 单独声明没负值是为了更好的说明
		Class<?> clazz; //关键的参数。就是你转换完成后的对象。
		String xmlStr; // 需要转换的xml
		// 实际的需要进行复制
		clazz=User.class; //当前需要转的的是user对象
		xmlStr=xml; // 需要转的xml内容为对应的userxml
		//jaxb 的声明 需要传入需要的class
		JAXBContext context = JAXBContext.newInstance(clazz);
		//就叫他转换器吧
		Unmarshaller unmarshaller = context.createUnmarshaller();
		//转reader
		StringReader sr = new StringReader(xmlStr);
		//进行转换  
		Object xmlObject = unmarshaller.unmarshal(sr);
		// 这里若需要具体使用某类型需要进行强转一下
		User u=(User)xmlObject;
		// 打印输出
		System.out.println(u.toString());
	}
	

这个需要补充说明一点。对于转java 对象。那么再数据对象类需要声明

@XmlRootElement(name = "User")

要么转换会失败无法找到对应的元素,

JAVA Object 转 xml

看代码吧。

	public static void main(String a[]) throws Exception {

		// java 对象 转 xml
		// 	创建一个数据对象
		User u = new User();
		u.setName("张三");
		// 根据数据对象构造一个jaxb对象
		JAXBContext context = JAXBContext.newInstance(User.class);
		 
		// 创建一个转换器
		Marshaller mar = context.createMarshaller();
		// 设置一下
		mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		// 创建一个字符串的输出流
		StringWriter sw = new StringWriter();
		// 开整
		mar.marshal(u, streamWriter);
		// 输出
		streamWriter.flush();
		// 关闭
		streamWriter.close();
		// 打印
		System.out.println(op.toString("UTF-8"));
	}

输出结果

<?xml version="1.0" ?><User><name>张三</name></User>

完成。

稍微复杂点的xml解析

xml稍作改动

<User id='root'>
<name id='1'>sss</name>
<cdata><![CDATA[ddddddd]]></cdata>
</User>

解析方法不变 主要是针对数据对象,进行了适当的改造。
User 数据对象

@ToString
@Getter
@XmlRootElement(name = "User")
public class User {
	String id;
	Name n;
 	String cdata;
	@XmlElement(name = "name")
	public void setN(Name n) {
		this.n = n;
	}
	@XmlAttribute(name = "id")
	public void setId(String id) {
		this.id = id;
	}
	@XmlElement(name = "cdata")
	public void setCdata(String cdata) {
		this.cdata = cdata;
	}
}

新增的Name 数据对象

@Getter
@ToString
public class Name {
	String id;
	String value;
	@XmlAttribute(name = "id")
	public void setId(String id) {
		this.id = id;
	}
	@XmlValue
	public void setValue(String value) {
		this.value = value;
	}
}

可以看到这些对象主要是在set方法中添加了相对应的注解。
具体都在javax.xml.bind.annotation 包下。
看看运行后输出结果

User(id=root, n=Name(id=1, value=sss), cdata=ddddddd)

可以看到完美的解析出来了。
用对象转xml也一样。将刚才的数据对象进行赋值操作然后。再进行转换
赋值代码

	    User u = new User();
		u.setId("uid");
		u.setCdata("data value");
		Name n=new Name();
		n.setId("nid");
		n.setValue("张三");
		u.setN(n);

执行后返回结果

<?xml version="1.0" ?>
<User id="uid">
<cdata>data value</cdata>
<name id="nid">张三</name>
</User>

可以看到除了CDATA 类型数据没有转换完成,其余就转换完成了。

CDATA 的操作

下来说说关于cdata的类型数据转xml 的操作
对于Cdata 的操作很简单,通过再反射过程中增加一个过程就行了。就是做一个Adapter
看代码

// 一个专门用于CData的处理,继承自XmlAdapter
public class CDataAdapter extends XmlAdapter<String, String> {
// 转对象时候拿到值需要怎么做
		@Override
		public String unmarshal(String v) throws Exception {
			return v;
		}
// 输出值的时候需要怎么做。这里只用加上CDATA 标签就行
		@Override
		public String marshal(String v) throws Exception {
			return new StringBuilder("<![CDATA[").append(v).append("]]>").toString();
		}
	}

然后对数据对象加上对应注释

	@XmlJavaTypeAdapter(CDataAdapter.class)

而user 对象中

    @XmlElement(name = "cdata")
	@XmlJavaTypeAdapter(CDataAdapter.class)
	public void setCdata(String cdata) {
		this.cdata = cdata;
	}

运行后获取结果

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User id="uid">
    <cdata>&lt;![CDATA[data value]]&gt;</cdata>
    <name id="nid">张三</name>
</User>

这里出现了对< > 尖括号的转义。那么需要对转义进行过滤。需要修改Marshaller 部分的代码。

对于Marshaller 部分需要进行

		// 不进行转义字符的处理
		marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
			public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer) throws IOException {
				writer.write(ch, start, length);
			}
		});

这里需要设置一个处理对象。CharacterEscapeHandler

com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler

如此后进行输出则完成需求
输出结果

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<User id="uid">
    <cdata><![CDATA[data value]]></cdata>
    <name id="nid">张三</name>
</User>

其他补充与说明。

对于注解部分与对应get set方法说明。

JAXBContext 创建的 Unmarshaller 与Marshaller 都是基于java 的反射实现的。
所以对于VO 类来说get 与set方法尤为重要。至于后面是什么没所谓。剩下的是依赖于注解来进行填充与调用。
举例:

	// 对于这个id 的解析。默认我们规范的都是setId
	@XmlAttribute(name = "id")
	public void  setId(String id)
	// 修改一下为setIddddddddddddd 这样。xml也是照样可以解析的
	@XmlAttribute(name = "id")
	public void  setIddddddddddddd(String id)
	// 但是这样 开始不为set 则无法解析。会报错
	@XmlAttribute(name = "id")
	public void  etId(String id)	

对于cdata 转义时候出现的问题

做cdata 的时。期初我的代码对于输出流默认是这样的:
摘录部分代码:

	ByteArrayOutputStream op = new ByteArrayOutputStream();
	XMLOutputFactory xof = XMLOutputFactory.newInstance();
	XMLStreamWriter streamWriter = xof.createXMLStreamWriter(op);
	Marshaller mar = context.createMarshaller();
	mar.marshal(u, streamWriter);

需要注意的是 通过factory 创建的这个XmlStreamWriter 是对 Marshaller 的setProperty 有影响的。具体为什么没有看。
这么创建writer 对于后续的代码

mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

等等设置是没有任何作用的。所以导致了对cdata 中的尖括号转义等是没有任何办法的。直接换成 StringWriter 后解决了这个问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值