对象与XML转换 - XStream - Dom4j

对象与XML相互转换

场景
当项目中需要使用XML报文格式转换时, 即可使用 XStream 或 dom4j 等技术进行解决.
常见的三种解析方式
(1)Dom : 文档对象模型 , 要求解析器将整个xml文档装进内存,并解析成document 对象
优点: 可增删改
缺点: 文档过大,会造成内存溢出
(2)SAX : 是一种速度更快的更有效的方法 (一行一行解析)
优: 处理快,可解析大文件
缺点: 只能读,逐渐释放资源 ,比较繁琐
(3) PULL 安卓内置解析方式,类似SAX
本文档主要介绍XSteam与Dom4j开发包的使用
环境 : 大家可根据自己需要去下载不同版本jar包 , 如找不到可联系博主
在这里插入图片描述

一 、XSteam
(一) XSteam 介绍
XStream是一个Java对象和XML相互转换的工具。
XStream对象相当Java对象和XML之间的转换器,转换过程是双向的。创建XSteam对象的方式很简单,只需要new XStream()即可。
(二) 常用方法
1.Java到xml,用toXML()方法。
2.Xml到Java,用fromXML()方法。
3.类别名,用alias(String name, Class type)。
4.类成员别名,用aliasField(String alias, Class definedIn, String fieldName) 。
5.类成员作为属性别名,用 aliasAttribute(Class definedIn, String attributeName, String alias),单独命名没有意义,还要通过useAttributeFor(Class definedIn, String fieldName) 应用到某个类上。
6.addImplicitCollection(Class ownerType, String fieldName),去掉集合类型生成xml的父节点。
7.registerConverter(Converter converter) ,注册一个转换器。
三 、XStream使用 (多层嵌套)

  1. User
public class User {
    private String u_id;   
    private String u_name;
    private String u_age;
	public String getU_id() {
		return u_id;
	}
	public void setU_id(String u_id) {
		this.u_id = u_id;
	}
	public String getU_name() {
		return u_name;
	}
	public void setU_name(String u_name) {
		this.u_name = u_name;
	}
	public String getU_age() {
		return u_age;
	}
	public void setU_age(String u_age) {
		this.u_age = u_age;
	}
	@Override
	public String toString() {
		return "User [u_id=" + u_id + ", u_name=" + u_name + ", u_age=" + u_age + "]";
	}
	
    
}
  1. Person
public class Person {
		private String p_personid;
	    private String P_name;
	    private String p_address;
	    private String p_tel;
	    private String p_email;
		public String getP_personid() {
			return p_personid;
		}
		public void setP_personid(String p_personid) {
			this.p_personid = p_personid;
		}
		public String getP_name() {
			return P_name;
		}
		public void setP_name(String p_name) {
			P_name = p_name;
		}
		public String getP_address() {
			return p_address;
		}
		public void setP_address(String p_address) {
			this.p_address = p_address;
		}
		public String getP_tel() {
			return p_tel;
		}
		public void setP_tel(String p_tel) {
			this.p_tel = p_tel;
		}
		public String getP_email() {
			return p_email;
		}
		public void setP_email(String p_email) {
			this.p_email = p_email;
		}
		@Override
		public String toString() {
			return "Person [p_personid=" + p_personid + ", P_name=" + P_name + ", p_address=" + p_address + ", p_tel="
					+ p_tel + ", p_email=" + p_email + "]";
		}
  1. Ren
public class Ren {
	private User user;
	private Person person;
	
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	@Override
	public String toString() {
		return "Ren [user=" + user + ", person=" + person + "]";
	}
	

}
	    
}
  1. 测试类
public class Xstream {
public static void main(String[] args) {
	User user = new User();
	user.setU_id("999");
	user.setU_name("ffef");
	user.setU_age("18");
	Person person = new Person();
	person.setP_personid("44");
	person.setP_name("si");
	person.setP_address("bj");
	person.setP_tel("5313");
	person.setP_email("66.com");
	Ren ren = new Ren();
	ren.setUser(user);
	ren.setPerson(person);
	

//	 XStream xStream = new XStream();
	// 注 : 如java类中含有_  , xstream 会转换成__ , 所以这种情况需要配置转换器
	 XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("__", "_")));
	 // 别名
	 xStream.alias("Ren",Ren.class);
	// 注 : 如果是嵌套设置别名 , 需要从父类设置别名
//	 xStream.alias("INTERFACE_INFO",User.class);
//	 xStream.alias("TONGDUN_INFO",Person.class);
	// 设置Person类的name成员别名Name
	xStream.aliasField("User", Ren.class, "user");
	xStream.aliasField("Person", Ren.class, "person");
	 // 忽略 字段 
//	 xStream.omitField(Ren.class,"user");
	 xStream.omitField(User.class,"u_id");
	 xStream.omitField(User.class,"u_name");
	 xStream.omitField(Person.class,"p_personid");
	 String s = xStream.toXML(ren);//得到xml文件
	 System.out.println(s);
}

}
  1. 结果输出
<Ren>
  <User>
    <u_age>18</u_age>
  </User>
  <Person>
    <P_name>si</P_name>
    <p_address>bj</p_address>
    <p_tel>5313</p_tel>
    <p_email>66.com</p_email>
  </Person>
</Ren>
  1. 需要注意的问题
    (1) 养成设置别名的习惯
    (2) java类字段含有"_" , XSteam 会转成 “__” .此时需要使用XSteam提供的转换器
    在这里插入图片描述
    (3) 嵌套设置别名需要从父类指定子类设置,不能直接用子类设置
    对象与XML转换 - XStream - Dom4j_第1张图片
    二、Xsream 转换器
    1. 问题描述 :
      使用XStream会自动忽略字段为null的节点
      当项目中使用时,某些字段要求显示无值的空节点
      这时以上操作不能满足需求 , 就需要添加一个Xstream转换器
      通过查阅一些相关资料,自己改了一个转换器,仅供参考
    2. 应用
      (1) 代码 (注意实体类注解)
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
	String name;
	int gender;
	int age;
	@XStreamAlias("address")
	Address address;
	@XStreamAlias("password")
	String PASSWORD;
	@XStreamAlias("username")
	String USERNAME;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getGender() {
		return gender;
	}
	public void setGender(int gender) {
		this.gender = gender;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	public String getPASSWORD() {
		return PASSWORD;
	}
	public void setPASSWORD(String pASSWORD) {
		PASSWORD = pASSWORD;
	}
	public String getUSERNAME() {
		return USERNAME;
	}
	public void setUSERNAME(String uSERNAME) {
		USERNAME = uSERNAME;
	}


}

注意 : 转换器需要定义所要转换的对象及所包含的对象名称

// 导包省略   
//注意 :  定义所要转换的对象及所包含的对象名称
public class MoreDataResultNullConverter implements Converter {
	@SuppressWarnings("rawtypes")
	private Class currentType;
	private final String clazzNames[] = { "User"};// 定义所要转换的对象及所包含的对象名称
	private List<String> clazzNamesList;

	@SuppressWarnings("rawtypes")
	@Override
	public boolean canConvert(Class type) {
		currentType = type;
		clazzNamesList = Arrays.asList(clazzNames);
		if (clazzNamesList.contains(currentType.getSimpleName())) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public void marshal(Object source, HierarchicalStreamWriter writer,
			MarshallingContext context) {
		try {
			marshalSuper(source, writer, context, currentType);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Object getObj(Class clazz, String nodeName, Object source)
			throws Exception {
		Method method = clazz.getMethod("get"
				+ Character
				.toUpperCase(nodeName.substring(0, 1).toCharArray()[0])
				+ nodeName.substring(1));
		Object obj = null;
		if (source != null) {
			obj = method.invoke(clazz.cast(source), new Object[0]);
		}
		return obj;
	}

	@SuppressWarnings({ "rawtypes" })
	private void objConverter(Object source, HierarchicalStreamWriter writer,
			MarshallingContext context, Class clazz, String nodeName,
			Class fieldClazz,String aliasName) throws Exception {
		Object obj = getObj(clazz, nodeName, source);
		if (StringUtils.isNotBlank(aliasName)) {
			nodeName=aliasName;
		}
		writer.startNode(nodeName);
		marshalSuper(obj, writer, context, fieldClazz);
		writer.endNode();
	}

	@SuppressWarnings({ "rawtypes" })
	private void collectionConverter(Object source,
			HierarchicalStreamWriter writer, MarshallingContext context,
			Class clazz, String nodeName, Field field,String aliasName) throws Exception {
		Type types[] = ((ParameterizedType) field.getGenericType())
				.getActualTypeArguments();
		Object obj = getObj(clazz, nodeName, source);
		if (StringUtils.isNotBlank(aliasName)) {
			nodeName=aliasName;
		}
		Collection collection = null;
		if (field.getType().equals(List.class)) {
			collection = (List) obj;
		} else if (field.getType().equals(Set.class)) {
			collection = (Set) obj;
		}
		writer.startNode(nodeName);
		for (Object object : collection) {
			String clazzName = ((Class) types[0]).getSimpleName();
			writer.startNode(Character.toLowerCase(clazzName.substring(0, 1)
					.toCharArray()[0]) + clazzName.substring(1));
			marshalSuper(object, writer, context, (Class) types[0]);
			writer.endNode();
		}
		writer.endNode();
	}

	@SuppressWarnings({ "rawtypes" })
	private void basicTypeConverter(Object source,
			HierarchicalStreamWriter writer, MarshallingContext context,
			Class clazz, String nodeName,String aliasName) throws Exception {
		Object obj = getObj(clazz, nodeName, source);
		if (StringUtils.isNotBlank(aliasName)) {
			nodeName=aliasName;
		}
		writer.startNode(nodeName);
		writer.setValue(obj == null ? "" : obj.toString());
		writer.endNode();
	}

	@SuppressWarnings({ "rawtypes" })
	private void marshalSuper(Object source, HierarchicalStreamWriter writer,
			MarshallingContext context, Class clazz) throws Exception {
		Field fields[] = clazz.getDeclaredFields();
		for (Field field : fields) {
			String nodeName = field.getName();
			XStreamAlias annotation=field.getAnnotation(XStreamAlias.class);
			String aliasName="";
			if (annotation!= null) {
				aliasName=annotation.value();
			}
			Class fieldClazz = field.getType();
			if (clazzNamesList.contains(fieldClazz.getSimpleName())) {
				objConverter(source, writer, context, clazz, nodeName,
						fieldClazz,aliasName);
			} else if (Arrays.asList(fieldClazz.getInterfaces()).contains(
					Collection.class)) {
				collectionConverter(source, writer, context, clazz, nodeName,
						field,aliasName);
			} else {
				basicTypeConverter(source, writer, context, clazz, nodeName,aliasName);
			}
		}
	}

	@Override
	public Object unmarshal(HierarchicalStreamReader reader,
			UnmarshallingContext context) {
		// TODO Auto-generated method stub
		return null;
	}

	public static void main(String[] args) {
		
		  User user= new User(); 
		  user.setName(null); 
		  user.setAge(28);
		  user.setGender(1);
		  XStream xStream = new XStream(new StaxDriver());
		  xStream.alias("A", User.class);
		  xStream.alias("address", Address.class);
		  xStream.registerConverter(new MoreDataResultNullConverter()); String
		  xml=xStream.toXML(user); System.out.println(xml);

	}
}

(2) 输出

<?xml version='1.0' encoding='UTF-8'?><A><name></name><gender>1</gender><age>28</age><address></address><password></password><username></username></A>

(3) 需要注意的问题
i . 转换器有可能会导致 之前定义的嵌套类子类别名失效
对象与XML转换 - XStream - Dom4j_第2张图片这时,在转换器中加入注解方式解决别名问题(加在实体类上)
对象与XML转换 - XStream - Dom4j_第3张图片
对象与XML转换 - XStream - Dom4j_第4张图片ii 多个嵌套的实体类 注意 循环引用 否则会造成内存溢出
可以将实体转换成新的DTO对象 将不使用的字段删除
iii 转换器可能会导致 字段忽略失效
如果想忽略字段,也可以在新的DTO 对象中将想忽略的字段删除进行解决
DTO 转换 请看本人的另一篇文章 – DTO的使用

三、Dom4j
(一) Dom4j介绍
用来解析XML文件
dom4j注释比较详细,下面就不一一介绍了,直接上代码
(二) 使用

  1. Person
public class Person {
    private String personid;
    private String name;
    private String address;
    private String tel;
    private String email;
	public String getPersonid() {
		return personid;
	}
	public void setPersonid(String personid) {
		this.personid = personid;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	@Override
	public String toString() {
		return "Person [personid=" + personid + ", name=" + name + ", address=" + address + ", tel=" + tel + ", email="
				+ email + "]";
	}
    
}
  1. 测试类
public class Test {
    public static void main(String[] args) {
   
    	
    	Person person2 = new Person();
    	Person person3= new Person();
    	person2.setPersonid("111");
    	person2.setName("小四");
    	person2.setAddress("北京");
    	person2.setTel("11111111111111111111");
    	person2.setEmail("66.com");
    	System.out.println(person2);
    	System.out.println("==========================================");
    	// 创建文档
    	Document doc = DocumentHelper.createDocument();
    	 // 创建节点
    	Element Person = doc.addElement("Person");
    	 // 创建子节点
		Element personid = Person.addElement("personid");
		Element name = Person.addElement("name");
		Element address = Person.addElement("address");
		Element tel = Person.addElement("tel");
		Element email = Person.addElement("email");
		// 设置节点属性
		Person.attributeValue("id", "1");
		name.attributeValue("id", "1");
		// 设置节点内容
		personid.setText("666");
		name.setText("小张");
		address.setText("北京");
		tel.setText("1100");
		email.setText("000.com");
		
		String a = null;
		
		String asXML = doc.asXML();
		System.out.println(asXML);
		System.out.println("===========================================");
//	    return getDocument(b).asXML();
		Document document = getDocument(person2);
		System.out.println(document);
		System.out.println("==========================================");
		String asXML2 = document.asXML();
		System.out.println(asXML2);
		System.out.println("=========================================");
		Object object = getObject(document,person3.getClass());
		System.out.println(object);
    }
    	}
  1. 补充测试类 (嵌套测试)
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

public class domTest {
	public static void main(String[] args) {
		User user = new User();
		user.setU_id("222");
		
//		测试空节点未赋值
//		user.setU_name("小五"); 
		
		user.setU_age("18");
		Person person = new Person();
		person.setP_personid("111");
		person.setP_name("小四");
		person.setP_address("北京");
		person.setP_tel("11111111111111111111");
		person.setP_email("66.com");
		
		// 创建文档
		Document createDocument = DocumentHelper.createDocument();
		// 创建节点
		Element addElement = createDocument.addElement("REN");
		// 创建子节点
		Element u = addElement.addElement("USER");
		// 创建子节点
		Element u1 = u.addElement("U_id");
		// *重点* 设置节点属性 如果有空节点需要显示 可以利用三元运算符进行显示
		u1.setText(user.getU_id()== null ? "" : user.getU_id());
		Element u2 = u.addElement("U_name");
		// *重点* 对象未赋值,显示空节点
		u2.setText(user.getU_name()==null? "" :user.getU_name());
		// *重点* 设置节点属性 如果空节点不想显示就不赋值
//		u.setText(user.getU_id());
//		u.setText(user.getU_name());
//		u.setText(user.getU_age());
		Element p = addElement.addElement("PERSON");
		Element p1 = p.addElement("P_address()");
		p1.setText(person.getP_address()== null ? "" :person.getP_address());
		Element p2 = p.addElement("P_tel()");
		// 如果不赋值 , 该节点是不显示的  , 可以指定字符串来显示空节点 , 或采用三元运算符
		p2.setText("");
//		p.setText(person.getP_personid());
//		p.setText(person.getP_name());
//		p.setText(person.getP_address());
//		p.setText(person.getP_tel());
//		p.setText(person.getP_email());
		// 转换
		String asXML = addElement.asXML();
		System.out.println(asXML);	
		
		
		// 如需要xml 标签头 可自行拼接
//		String data = ""+asXML;
	}

}

输出结果

<REN><USER><U_id>222</U_id><U_name></U_name></USER><PERSON><P_address()>北京</P_address()><P_tel()></P_tel()></PERSON></REN>
  1. 抽调的方法 - 对象转xml
public static Document getDocument(Object b) {
		Document document = DocumentHelper.createDocument();
		// 创建根节点元素
		Element root = document.addElement(b.getClass().getSimpleName());
		try {
			Field[] field = b.getClass().getDeclaredFields(); // 获取实体类b的所有属性,返回Field数组
			for (int j = 0; j < field.length; j++) { // 遍历所有有属性
				String name = field[j].getName(); // 获取属属性的名字
				if (!name.equals("serialVersionUID")) {// 去除串行化序列属性
					name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写,方便构造get,set方法
					Method m = b.getClass().getMethod("get" + name);
					// System.out.println("属性get方法返回值类型:" + m.getReturnType());
					String propertievalue = (String) m.invoke(b);// 获取属性值
					Element propertie = root.addElement(name);
					propertie.setText(propertievalue);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return document;
	}
  1. 抽调的方法 - xml转对象
public static Object getObject(Document document, Class<?> clazz) {
		Object obj = null;
		// 获取根节点
		Element root = document.getRootElement();
		try {
			obj = clazz.newInstance();// 创建对象
			List<Element> properties =  root.elements();
			for (Element pro : properties) {
				// 获取属性名(首字母大写)
				String propertyname = pro.getName();
				String propertyvalue = pro.getText();
				Method m = obj.getClass().getMethod("set" + propertyname, String.class);
				m.invoke(obj, propertyvalue);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return obj;
	}
  1. 抽调的方法 - xml 转 String
	public static String docOrStr(Document document) {
		String asXML = document.asXML();
		return asXML;
	}

三、 总结
XStream 默认使用xpp3 XML解析器 (需导包) , 性能方面Xtream强于dom4j ,所以最后选用XSream 来实现, 但是大家在选择技术的同时也要考虑自己项目的实际情况 .
本文还有很多不足 , 请大家多多关照 !
————————————————

你可能感兴趣的:(业务解决方案专栏,java)