java 序列化 xml

181 阅读3分钟

报文

协议

名称定义
起始标志符EB90(小头字节序)
发送会话序列号long(八字节小头字节序)
接收会话序列号long(八字节小头字节序)
会话源标识0x00(一个字节)
xml的字节长度int(四字节小头字节序)
交互内容(xml格式)xml,字符编码为UTF-8
结束标志符号EB90(小头字节序)

交互内容xml格式

名称说明
SendCode发送方唯一标识
ReceiveCode接收方唯一标识
Type消息类型
Code目标对象唯一标识
Time时间(格式:yyyy-MM-dd hh:mm:ss)
Items消息内容(可选)

示例

<?xml version="1.0" encoding="UTF-8"?>
<PatrolHost>
    <SendCode>Server01</SendCode>
    <ReceiveCode>Client01</ReceiveCode>
    <Type>251</Type>
    <Code>200</Code>
    <Command>4</Command>
    <Time>2023-01-01 12:02:35</Time>
    <Items>
        <Item name="name01" age="12" group="group01" />
        <Item name="name02" age="22" group="group02" />
    </Items>
</PatrolHost>

实现

需求

  1. 能通过注解来定制xml的节点名称
  2. 不操作document,而是通过序列化,反序列化来操作实体

工具类

  1. JAXB
  2. 支持注解来定义xml节点名称
  3. 不操作document,通过对象来实现,序列化与反序列化

代码

// 最外层协议
public abstract class TcpMessage{
    public TcpMessage(){}
    
    public TcpMessage(long sessionId, long receiveSessionId, int sessionType, int xmlLength, byte[] xmlBody){
        this.sessionId = sessionId;
        this.receiveSessionId = receiveSessionId;
        this.sessionType = sessionType;
        this.xmlLength = xmlLength;
        this.xmlBody = xmlBody;
    }
    
    // 发送会话id
    private long sessionId;
    
    // 接收会话id
    private long receiveSessionId;
    
    // 会话源标识
    private int sessionType;
    
    // xml报文长度
    private int xmlLength;
    
    // xml报文内容
    private byte[] xmlBody;
}

// xml实体
@XmlRootElement(name="PatrolHost") // 指定xml根结点名字为PatrolHost
@XmlAccessorType(XmlAccessType.PROPERTY) //xml序列化时,根据属性来序列化
@XmlType(propOrder={"sendCode","receiveCode","type","code","command","time","items"}) // 序列化的顺序
public class XmlBody {
    // 发送方唯一标识
    private String sendCode;
    
    // 接收方唯一标识
    private String receiveCode;
    
    // 消息类型
    private String type;
    
    // 消息编码
    private String code;
    
    // 消息命令行编码
    private String command;
    
    // 发送时间
    private String time;
    
    // 消息items项
    private List<HashMap<String,String>> items;
    
    @XmlElement(name="SendCode") // 属性序列化时的名字
    public String getSendCode(){ return sendCodel; }
    
    public void setSendCode(String sendCode){this.sendCode = sendCode;}
    
    @XmlElement(name="ReceiveCode") // 属性序列化时的名字
    public String getReceiveCode(){return receiveCode;}
    
    public void setreceiveCode(){this.receiveCode = receiveCode;}
    
    @XmlElement(name="Type") // 属性序列化时的名字
    public String getType(){return type;}
    
    public void setType(String type){this.type=type;}
    
    @XmlElement(name="Code") // 属性序列化时的名字
    public String getCode(){return code;}
    
    public void setCode(String code){this.code = code;}
    
    @XmlElement(name="Command") // 属性序列化时的名字
    public String getCommand(){return command;}
    
    public void setCommand(String command){this.command = command;}
    
    @XmlElement(name="Time") // 属性序列化时的名字
    public String getTime(){return time;}
    
    public void setTime(String time){this.time = time;}
    
     @XmlElement(name="Time") // 属性序列化时的名字
    // 如果要序列化Map,那么就必须是实际类型,不可用接口,所以这里是HashMap
    @XmlJavaTypeAdapter(ItemsXmlAdapter.class) // List<HashMap<String,String>>的序列化器(需要自己实现)
    public List<HashMap<String,String>> getItems{return items;}
    
    public void setItems(List<HashMap<String,String>> items){this.items = items;}
}

// List<HashMap<String,String>>的序列化器
public class ItemXmlAdapter extends Xmladapter<Object, List<HashMap<String,String>>>{
    // 反序列化方法
    @Override
    public List<HashMap<String,String>> unmarshal(Object rowsElement) throws Exception{
        if(rowsElement==null){
            return null;
        }
        
        Element rowsEle = (Element) rowsElement;
        
        // 判断是否有HashMap数据
        NodeList rowNodes = rowsEle.getChildNodes();
        int rowCount = rowNodes.getLength();
        if(rowCount==0){
            return null;
        }
        
        // 转换List<HashMap<String,String>>
        List<HashMap<String,String>> result = new ArrayList<>();
        for(int i=0; i<rowCount; i++){
            Node rowNode = rowNodes.item(i);
            
            // 只查询item节点,排除其余节点
            if(!rowNode.getNodeName().equalsIgnoreCase("item")){
                continue;
            }
            
            // 判断item中是否有attribute
            NamedNodeMap attributes = rowNode.getAttributes();
            if(attributes.getLength()==0){
                continue;
            }
            
            HashMap<String,String> map = new HashMap<>();
            for(int j=0;j<attributes.getLength();j++){
                Node item = attributes.item(j);
                String key = item.getNodeName();
                String value = item.getNodeValue();
                map.put(key,value);
            }
            
            result.add(map);
        }
        
        return result;
    }
    
    // 序列化
    @Override
    public Object marshal(List<HashMap<String,String>> items) throws Exception{
        // 最外层的Items
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();
        Element rootElement = document.createElement("Items");
        document.appendChild(rootElement);
        
        // Items中添加Item
        if(CollectionUtils.isNotEmpty(items)){
            for(HashMap<String,String> map : items){
                if(CollectionUtils.isEmpty(map)){
                    continue;
                }
                
                /**
                 * 组装数据
                 * <Items>
                 *     <Item user="" name="" group="" />
                 * </Items>
                Element itemElement = document.createElement("Item");
                rootElement.appendChild(itemElement);
                for(Map.Entry<String,String> entry : map.entrySet()){
                    Attr attr = document.createAttribute(entry.getKey());
                    attr.setValue(entry.getValue());
                    itemElement.setAttributeNode(attr);
                }
            }
        }
        
        return rootElement;
        
    }
}

// 序列化方法
public interface Serializer{
    <T> T deserialize(Class<T> clazz, byte[] bytes);
    
    <T> byte[] serialize(T object, Class<T> clazz);
    
    enum Algorithm implements Serializer{
        XML{
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes){
                JAXBContext jaxbContext = null;
                // 我这里会有很多对象派生自XmlBody,所以有这个if分支。如果你不需要派生类,就不用写这个if分支了,直接else分支即可
                if(XmlBody.class.isAssignableFrom(clazz)){
                    jaxbContext = JAXBContext.newInstance(XmlBody.class, clazz); // 把clazz的父类XmlBody.class写入,这样clazz与XmlBody就有父子关系
                }else{
                    jaxbContext = JAXBContext.newInstance(clazz);
                }
                Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
                InputStream inputstream = new ByteArrayInputStream(bytes);
                return (T) unmarshaller.unmarshal(inputStream);
            }
            
            @Override
            public <T> byte[] serialize(T object, Class<T> clazz){
                JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
                Marshaller marshaller = jaxbContext.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                marshaller.setProperty(Marshaller.JAXB_FORGMENT, true);
                StringWriter writer = new StringWriter();
                // 这样代码就是为了去掉一个xml自动序列化时,头文件中会多一个元素(这个元素并不会影响序列化与反序列化,我这里就是为了完全与定义的报文一致,所以才去掉)
                writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+"\n");
                marshaller.marshal(object, writer);
                return writer.toString().getBytes();
            }
        }
    }
}