【设计模式】一个Soap解析居然这样进行拆解代码思路之--逗比经理居然找Egan闲聊

原创
2021/03/04 12:30
阅读数 30

Egan闲了一段时间后,逗比经理居然来找Egan闲聊了

逗比经理居然说爱上Egan……

Egan摸了摸自己的刘海

又是一个不眠夜

Egan看着SOAP文件,想起前段时间刚学的【使用命令wsimport构建WebService客户端】,咦,是不是可以用用。

只要来一个soap文件我就生成一次对应的Java客户端,我真是太聪明了

那我手动处理?不行不行,我有点偏题了。

我在看看这个soap对应的xml文件

soap wsdl文件大概的样子是这样的

整理了一下,大概有几种需要解析的节点类型

 

 

 

 

 

 

 

但是呢,soap一切是从service节点开始的,从服务(service)中拿到地址(address)进行请求。

整个类的结构理出来差不多就是以下的类图结构了,从下到上看过来,把所有可能出现的情况罗列出来了.

但是每个节点在xml里面是属于同级结构,还需要把他们串起来,才能达到以上类图的样子。

那必须有以下两个步骤

  1. 解析所有相关的节点

  2. 将所以节点关联起来

除了以上两个步骤外,每个节点都有自己的属性:name

那么以此可以得到每个节点都必须有一个属性两个动作

得到一个接口

public interface SoapElement {
    /**
     * 获取节点名称
     *
     * @return 节点名称
     */
    String getName();
    /**
     * 解析
     *
     * @param node 需要解析的节点
     * @return 解析好的节点
     */
    SoapElement parsing(Element node);

    /**
     * 关联自身内部对象,需要对应的内部对象
     *
     * @param elements 所有的属性节点
     */
    void relate(Map<String, SoapElement> elements);

}

接着每个节点都有自己的name并且都是取节点上的name属性操作,那么以此可以得到一个设置获取name的动作的基础节点

public abstract class BaseElement implements SoapElement {
    protected final static String NAME = "name";
    protected String name;
    @Override
    public String getName() {
        return name;
    }
    /**
     * 设置节点名称
     * @param name 名称
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 获取“:”后面的值,用于获取节点类型名,
     * 如wsdl:message 那么返回 message 
     * @param value 转化前
     * @return 转化后
     */
    public static final String getValue(String value) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        return value.substring(value.indexOf(":") + 1);
    }
}

定义Element节点类,只负责解析Element节点与Element子节点得到所有的参数名与参数类型

跟着上面类图来从上到下

接下来Types类,一个Types对应多个Element

Message类,这里可能有parts子节点,这个子节点直接存放参数名与参数类型,根据节点判断是否需要Element

Message分为两部分,输入与输出并存放至Operation类中

然后Operation解析来自于PortType。

接着Binding

最后一个service

所有的组件编写完成,完工

解析每一个节点的时候需要通过名字去转换成上面的节点组件,一个一个if去判断好像一点都不优雅。

 Element item = (Element) node;
 String tagName = BaseElement.getValue(item.getTagName());
 SoapElement element = null;
 if ("types".equals(tagName)) {
     element = new Types();
 }else   if ("element".equals(tagName)) {
     element = new com.egzosn.contract.xml.soap.bean.Element();
 }else   if ("message".equals(tagName)) {
      element = new Message();
 }

咦!写个枚举类吧,并且定义解析方法,代理下每个组件的解析方法

\

最后写解析并获取根节点获取Service的入口

/**
     * 获取SOAP服务
     * @param content SOAP的xml内容
     * @return SOAP服务
     */
    public static final Service getService(InputStream content) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document doc = documentBuilder.parse(content);
        doc.getDocumentElement().normalize();
        return getService(doc.getDocumentElement());
    }
    /**
     * 通过element获取 soap服务
     * @param root soap根节点
     * @return soap服务
     */
    public static final Service getService(Element root) {
        if (!root.hasChildNodes()) {
            return null;
        }
        NodeList childNodes = root.getChildNodes();
        //用与存储所有的soap节点
        Map<String, SoapElement> soapElements = new HashMap<String, SoapElement>();
        Service service = null;
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            Element item = (Element) node;
            String tagName = BaseElement.getValue(item.getTagName());
            SoapChild wsdlChild = SoapChild.valueOf(tagName);
            SoapElement element = wsdlChild.parsing(item);
            if (SoapChild.service == wsdlChild) {
                service = (Service) element;
                service.setNamespace(root.getAttribute("targetNamespace"));
            }
            soapElements.put(tagName + ":" + element.getName(), element);
        }
        //将所有的节点串连起来
        for (SoapElement element : soapElements.values()) {
            element.relate(soapElements);
        }
        return service;
    }

大功告成!

唉!这个点了

收拾收拾,明早写下把数据进行入库吧,撑不住了

______________________________________END_______________________________________

有兴趣的同学可以留言分享

最近小E正在学习小程序相关的知识并开发了自己的小程序,有兴趣的同学可以加小E的VX并备注“小程序”,小E的VX: Egzasn

也可以关注小E的公众号: 墨鲸说话

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部