Swagger文档转Word 文档

GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer

原创作品,转载请注明出处:http://www.cnblogs.com/jmcui/p/8298823.html

一、前言

为什么会产生这个需求呢?

我们公司作为乙方,老是被客户追着要一份API文档,当我们把一个 Swagger 文档地址丢给客户的时候。客户还是很不满意,嫌不够正式!!死活坚持要一份 word 文档 。然后领导给了个接口模板,就把这个活交给我了......我去,近10个微服务,几百个接口,这不得要了我的命啊(最后整理出来将近200页的 word 文档)。最后,还是领导有办法:要不我们把Swagger的 json文件转成word文档吧!

一直坚持一句话。作为使用者,人要迁就机器;作为开发者,要机器迁就人。

二、思路

领导提供了一个接口模板,类似下面这样,其实就是一个word的table页。想到 html 可以转 word ,那么问题就变成了 :

1、解析JSON 文件

2、把JSON文件的内容填充进html 的Table中

3、由html直接转成word

几百个接口,一气呵成!如下,还有一个简单的示例,就是请求参数 和 返回值 。怎么处理呢?在程序中写了 HTTP 的请求,封装了需要的参数去执行了一个请求,得到相应的返回值!

三、实现

1、封装对象

按照面向对象的思想,一个接口Table就是一个对象,可变的请求参数和返回参数也封装成一个对象......

public class Table {

    /**
     * 大标题
     */
    private String title;
    /**
     * 小标题
     */
    private String tag;
    /**
     * url
     */
    private String url;

    /**
     * 响应参数格式
     */
    private String responseForm;

    /**
     * 请求方式
     */
    private String requestType;

    /**
     * 请求体
     */
    private List<Request> requestList;

    /**
     * 返回体
     */
    private List<Response> responseList;

    /**
     * 请求参数
     */
    private String requestParam;

    /**
     * 返回值
     */
    private String responseParam;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getResponseForm() {
        return responseForm;
    }

    public void setResponseForm(String responseForm) {
        this.responseForm = responseForm;
    }

    public String getRequestType() {
        return requestType;
    }

    public void setRequestType(String requestType) {
        this.requestType = requestType;
    }

    public List<Request> getRequestList() {
        return requestList;
    }

    public void setRequestList(List<Request> requestList) {
        this.requestList = requestList;
    }

    public List<Response> getResponseList() {
        return responseList;
    }

    public void setResponseList(List<Response> responseList) {
        this.responseList = responseList;
    }

    public String getRequestParam() {
        return requestParam;
    }

    public void setRequestParam(String requestParam) {
        this.requestParam = requestParam;
    }

    public String getResponseParam() {
        return responseParam;
    }

    public void setResponseParam(String responseParam) {
        this.responseParam = responseParam;
    }
}

Table

public class Request {

    /**
     * 请求参数
     */
    private String description;

    /**
     * 参数名
     */
    private String name;

    /**
     * 数据类型
     */
    private String type;

    /**
     * 参数类型
     */
    private String paramType;

    /**
     * 是否必填
     */
    private Boolean require;

    /**
     * 说明
     */
    private String remark;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Boolean getRequire() {
        return require;
    }

    public void setRequire(Boolean require) {
        this.require = require;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public String getParamType() {
        return paramType;
    }

    public void setParamType(String paramType) {
        this.paramType = paramType;
    }
}

Request

public class Response {
    /**
     * 返回参数
     */
    private String description;

    /**
     * 参数名
     */
    private String name;

    /**
     * 说明
     */
    private String remark;

    public Response(String description, String name, String remark) {
        this.description = description;
        this.name = name;
        this.remark = remark;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }
}

Response

2、解析 json

先来看看Swagger json文件的格式吧!需要注意的是这个 json 文件默认的 host 是没有加 http:// 前缀的,需要我们手动加上,因为程序的HTTP请求不像浏览器一样会自动补上 http:// 的前缀 ......

解析JSON真是一件枯燥的工作,大家可以按照自己想要生成模板的样子修改这边的代码......需要提的是,这里有一点让我纠结了好久。怎么伪造接口的请求参数发送HTTP请求以避免不会抛异常呢?最后还是参考了Swagger的方式,即:如果是 String 类型的参数,就把这个参数置为"string";如果是 Integer 类型的参数,就把这个参数置为 0 ;如果是Double 类型的参数,就置为 0.0 ;如果是其他没办法预见的类型,就全部置为 null;

解析 JSON 用的是Spring推荐的 jackson ,这部分感觉没什么好说的,直接上代码吧!

@Service
public class TableServiceImpl implements TableService {

    @Override
    public List<Table> tableList() {
        List<Table> list = new LinkedList();
        try {
            ClassLoader classLoader = TableService.class.getClassLoader();
            URL resource = classLoader.getResource("data.json");
            Map map = new ObjectMapper().readValue(resource, Map.class);
            //得到host,用于模拟http请求
            String host = String.valueOf(map.get("host"));
            //解析paths
            LinkedHashMap<String, LinkedHashMap> paths = (LinkedHashMap) map.get("paths");
            if (paths != null) {
                Iterator<Map.Entry<String, LinkedHashMap>> iterator = paths.entrySet().iterator();
                while (iterator.hasNext()) {
                    Table table = new Table();
                    List<Request> requestList = new LinkedList<Request>();
                    String requestType = "";

                    Map.Entry<String, LinkedHashMap> next = iterator.next();
                    String url = next.getKey();//得到url
                    LinkedHashMap<String, LinkedHashMap> value = next.getValue();
                    //得到请求方式,输出结果类似为 get/post/delete/put 这样
                    Set<String> requestTypes = value.keySet();
                    for (String str : requestTypes) {
                        requestType += str + "/";
                    }
                    Iterator<Map.Entry<String, LinkedHashMap>> it2 = value.entrySet().iterator();
                    //解析请求
                    Map.Entry<String, LinkedHashMap> get = it2.next();//得到get
                    LinkedHashMap getValue = get.getValue();
                    String title = (String) ((List) getValue.get("tags")).get(0);//得到大标题
                    String tag = String.valueOf(getValue.get("summary"));
                    //请求体
                    ArrayList parameters = (ArrayList) getValue.get("parameters");
                    if (parameters != null && parameters.size() > 0) {
                        for (int i = 0; i < parameters.size(); i++) {
                            Request request = new Request();
                            LinkedHashMap<String, Object> param = (LinkedHashMap) parameters.get(i);
                            request.setDescription(String.valueOf(param.get("description")));
                            request.setName(String.valueOf(param.get("name")));
                            request.setType(String.valueOf(param.get("type")));
                            request.setParamType(String.valueOf(param.get("in")));
                            request.setRequire((Boolean) param.get("required"));
                            requestList.add(request);
                        }
                    }
                    //返回体,比较固定
                    List<Response> responseList = listResponse();
                    //模拟一次HTTP请求,封装请求体和返回体,如果是Restful的文档可以再补充
                    if (requestType.contains("post")) {
                        Map<String, String> stringStringMap = toPostBody(requestList);
                        table.setRequestParam(stringStringMap.toString());
                        String post = NetUtil.post(host + url, stringStringMap);
                        table.setResponseParam(post);
                    } else if (requestType.contains("get")) {
                        String s = toGetHeader(requestList);
                        table.setResponseParam(s);
                        String getStr = NetUtil.get(host + url + s);
                        table.setResponseParam(getStr);
                    }

                    //封装Table
                    table.setTitle(title);
                    table.setUrl(url);
                    table.setTag(tag);
                    table.setResponseForm("application/json");
                    table.setRequestType(StringUtils.removeEnd(requestType, "/"));
                    table.setRequestList(requestList);
                    table.setResponseList(responseList);
                    list.add(table);
                }
            }
            return list;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //封装返回信息,可能需求不一样,可以自定义
    private List<Response> listResponse() {
        List<Response> responseList = new LinkedList<Response>();
        responseList.add(new Response("受影响的行数", "counts", null));
        responseList.add(new Response("结果说明信息", "msg", null));
        responseList.add(new Response("是否成功", "success", null));
        responseList.add(new Response("返回对象", "data", null));
        responseList.add(new Response("错误代码", "errCode", null));
        return responseList;
    }

    //封装post请求体
    private Map<String, String> toPostBody(List<Request> list) {
        Map<String, String> map = new HashMap<>(16);
        if (list != null && list.size() > 0) {
            for (Request request : list) {
                String name = request.getName();
                String type = request.getType();
                switch (type) {
                    case "string":
                        map.put(name, "string");
                        break;
                    case "integer":
                        map.put(name, "0");
                        break;
                    case "double":
                        map.put(name, "0.0");
                        break;
                    default:
                        map.put(name, "null");
                        break;
                }
            }
        }
        return map;
    }

    //封装get请求头
    private String toGetHeader(List<Request> list) {
        StringBuffer stringBuffer = new StringBuffer();
        if (list != null && list.size() > 0) {
            for (Request request : list) {
                String name = request.getName();
                String type = request.getType();
                switch (type) {
                    case "string":
                        stringBuffer.append(name+"&=string");
                        break;
                    case "integer":
                        stringBuffer.append(name+"&=0");
                        break;
                    case "double":
                        stringBuffer.append(name+"&=0.0");
                        break;
                    default:
                        stringBuffer.append(name+"&=null");
                        break;
                }
            }
        }
        String s = stringBuffer.toString();
        if ("".equalsIgnoreCase(s)){
            return "";
        }
        return "?" + StringUtils.removeStart(s, "&");
    }
}

TableServiceImpl

3、html 模板

我们需要一个和 Word Table 模板一样的HTML 页面,然后利用JSP的 foreach 遍历后台得到的 List<Table> 集合,一气呵成,生成所有接口......

<%-- text/html:正常的html显示  application/msword:html页面直接转word--%>
<%@ page contentType="application/msword" pageEncoding="UTF-8" language="java" %>
<%--<%@page contentType="text/html" pageEncoding="UTF-8" language="java" %>--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <title>tool</title>
        <style type="text/css">
            .bg {
            background-color: rgb(84, 127, 177);
            }

            tr {
            height: 20px;
            font-size: 12px;
            }

            .specialHeight {
            height: 40px;
            }
        </style>
    </head>
    <body>
        <div style="width:800px; margin: 0 auto">
            <c:forEach items="${table}" var="t">
                <h4>${t.title}</h4> <%--这个是类的说明--%>
                <h5>${t.tag}</h5>   <%--这个是每个请求的说明,方便生成文档后进行整理--%>
                <table border="1" cellspacing="0" cellpadding="0" width="100%">
                    <tr class="bg">
                        <td colspan="6"><c:out value="${t.tag}"/></td>
                    </tr>
                    <tr>
                        <td>URL</td>
                        <td colspan="5">${t.url}</td>
                    </tr>
                    <tr>
                        <td>请求方式</td>
                        <td colspan="5">${t.requestType}</td>
                    </tr>
                    <tr>
                        <td>返回值类型</td>
                        <td colspan="5">${t.responseForm}</td>
                    </tr>

                    <tr class="bg" align="center">
                        <td>请求参数</td>
                        <td>参数名</td>
                        <td>数据类型</td>
                        <td>参数类型</td>
                        <td>是否必填</td>
                        <td>说明</td>
                    </tr>
                    <c:forEach items="${t.requestList}" var="req">
                        <tr align="center">
                            <td>${req.description}</td>
                            <td>${req.name}</td>
                            <td>${req.type}</td>
                            <td>${req.paramType}</td>
                            <td>
                                <c:choose>
                                    <c:when test="${req.require == true}">Y</c:when>
                                    <c:otherwise>N</c:otherwise>
                                </c:choose>
                            </td>
                            <td>${remark}</td>
                        </tr>
                    </c:forEach>
                    <tr class="bg" align="center">
                        <td>返回参数</td>
                        <td>参数名</td>
                        <td colspan="4">说明</td>
                    </tr>

                    <c:forEach items="${t.responseList}" var="res">
                        <tr align="center">
                            <td>${res.description}</td>
                            <td>${res.name}</td>
                            <td colspan="4">${res.remark}</td>
                        </tr>
                    </c:forEach>

                    <tr class="bg">
                        <td colspan="6">示例</td>
                    </tr>
                    <tr class="specialHeight">
                        <td class="bg">请求参数</td>
                        <td colspan="5">${t.requestParam}</td>
                    </tr>
                    <tr class="specialHeight">
                        <td class="bg">返回值</td>
                        <td colspan="5">${t.responseParam}</td>
                    </tr>
                </table>
                <br>
            </c:forEach>
        </div>
    </body>
</html>

json.jsp

4、效果

把代码运行起来后,访问JSP页面,不会像平常一样看到 HTML 页面,而是直接下载生成一个 文件,按照SpringMVC请求方法命名(这个项目中是getJson文件)。把这个文件的后缀名改成 .doc 就能看到效果了!差不多是如下效果:

当然,剩下的工作,就要我们手动去整理维护了。比如:把属于同一个类的请求分类整理到一起;把HTTP请求错误的返回值删除(还无法适配所有的HTTP请求情况);整理维护效果如下:

四、使用

如果直接采用我的API文档模板的话,只需要将 resources 目录下的 data.json 文件的内容替换成自己的Swagger Json 文件内容就好。但是,考虑到我们模板中的返回参数是我们公司一个自定义的对象,所以可能这里还需要大家根据自己的要求稍作修改,主要 修改TableServiceImpl 类下的 listResponse() 方法。

需要说明的是,这个项目还没有很好的支持所有的HTTP请求,比如 restful 服务将请求参数放在请求路径中的;比如参数是放在header中的;以及一系列可能没有考虑到的bug......

另外,我觉得 TableServiceImpl  还有很大可以改善的地方,代码略显冗余。之后慢慢维护吧!当然,很欢迎大家一起来开发...哈哈

五、结语

一直觉得,IT最迷人的地方就是开源和分享,大家互不相识,即使没有利益可图,却能为同一个项目,相同的目标 贡献自己的时间和精力。想想就不可思议。写这个博文的目地更多是分享自己的创意和想法,说实话,代码可能写的有点烂。还请大家不要嫌弃,不吝指教!

原文地址:https://www.cnblogs.com/jmcui/p/8298823.html

时间: 2024-11-08 23:47:58

Swagger文档转Word 文档的相关文章

使用Solid Converter PDF控件把PDF转换至Word文档

Solid Converter PDF可以准确地将PDF格式的文档转换为Word (DOC)格式,并且保留文本.图像.页面布局.表格.标题 /页脚等内容. 入门指南:将一个PDF文档转换为Word文档,只需: 1. 在PDF格式转换工具打开PDF文件 2. 选择“Convert to Word”按钮 3.进行转换 您还可以单击“Convert PDF to Word”链接到PDF格式转换工具中开始面板页面 高级选项.使用高级选项对话窗口将PDF文档转换为Word文档,只需查看“Prompt fo

spring mvc 下 word 文档(含图片)导出

最近在处理 word 文档导出工作,整理并总结下. 经过一番百度和亲测,大部分人使用的都是iText,iReport等...当我去尝试用这几种方法的时候,要实现我的需求可以,但是代码量太大了~~~因为我的 word 文档结构比较复杂,内容较多,有点懒得去写.于是我寻求通过jsp 或 javascript 页面的方式导出,这样子格式都直接在web页面上已经编辑好了,不许通过代码再转成word的形式. javascript 方式的话需要使用到ActiveXObject,这样子对浏览器是有要求的~~直

C# 如何合并、拆分Word文档

概述 出于方便文档管理.存储.传输等目的,我们常会想要将某些文档拆分为多个子文档,或者将多个文档合并为一个文档.在本文中,将介绍对Word文档进行拆分.合并的方法.下面的示例中将包含以下要点: 合并Word文档1.1 新建一页合并到文档1.2 紧接上文合并到文档 拆分Word文档2.1 按分节符拆分2.2 按分页符拆分 所用工具 Free Spire.Doc for .NET 6.3 Visual Studio 示例操作 一.合并Word文档(一)以新建一页合并到文档[C#] using Spi

将ppt幻灯片转为Word文档的简单应用

工作之后你就会明白做好ppt的重要性,因为工作中需要制作ppt的时候很多.有时候我们也会遇到要将ppt转换成word的时候, PPT幻灯片文件的应用特别广泛,上学时老师上课会用它辅助讲解,上班后会议室的老板会用它展示他想表达的关键词.同时,不仅可以在计算机上进行演示,也可以在投影仪上演示,也可以把演示文稿打印出来,制作成胶片.如果我们需要将PPT转换成Word,该怎么转换呢? 下面就和大家简单说说转换的步骤. 1.先下载一个pdf转换器,打开下载好的软件,在主页面的左侧需要选择文件类型,这里我们

怎样能够轻松将ppt转为word文档

现在的办公中,有三个最常用的办公文档就是Word.Excel.PPT,三者间也存在着密切的关系,一般情况下 我们会把制作好的PPT文档转换成成Word文档以便携带和交流.相信很多人都想知道方法,但对于小白来说,看到一大堆资料时,都会头疼不已,那怎样可以很好大将ppt转换成word文档?下面就跟大家具体的介绍下如何来转换文档格式. 这里需要应用到文档的格式转换工具--迅捷pdf转换器.通常来说,转换单个PDF文件几乎可以在瞬间完成,不会感受到任何转换过程的停顿,即便针对超大容量以及复杂的内容的PD

怎么把Word文档转为PDF格式

怎么把Word文档转为PDF格式?现在的人大多数都喜欢用PDF格式来进行阅读,这是因为PDF文件有不错的视觉阅读性和保护性.但对于文员编辑来说,Word文档才是他们编辑资料的主要工具.有时候为了保障文件资料的安全,我们需要把编辑好Word文档转换成PDF格式,这样做主要是为了方便阅读和进行编辑和修改,同时大大的提高了我们的工作效率.那么问题来了,怎么把Word文档转为PDF格式呢?下面我们就一起来看看下面的介绍! 其实我们只需要一款专业的转换器就可以将Word文档转换成PDF格式."迅捷PDF转

用Aspose.Words for .NET动态生成word文档中的数据表格

1.概述 最近项目中有一个这样的需求:导出word 文档,要求这个文档的格式不是固定的,用户可以随便的调整,导出内容中的数据表格列是动态的,例如要求导出姓名和性别,你就要导出这两列的数据,而且这个文档不是导出来之后再调整而是导出来后已经是调整过了的.看到这里,您也许马上想到用模板导出!而且.NET中自带有这个组件:Microsoft.Office.Interop.Word,暂且可以满足需求吧.但这个组件也是有局限性的,例如客户端必须装 office组件,而且编码复杂度高.最麻烦的需求是后面那个-

Java生成word文档

itext-rtf-2.1.7.jar,下载地址:http://download.csdn.net/detail/xuxu198899223/7717727 itext-2.1.7.jar 下载地址:http://download.csdn.net/detail/xuxu198899223/7717717 package word; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arra

黄聪:利用Aspose.Word控件实现Word文档的操作(转)

撰写人:伍华聪  http://www.iqidi.com  Aspose系列的控件,功能都挺好,之前一直在我的Winform开发框架中用Aspose.Cell来做报表输出,可以实现多样化的报表设计及输出,由于一般输出的内容比较正规化或者多数是表格居多,所以一般使用Aspose.Cell来实现我想要的各种Excel报表输出.虽然一直也知道Aspose.Word是用来生成Word文档的,而且深信其也是一个很强大的控件,但一直没用用到,所以就不是很熟悉. 偶然一次机会,一个项目的报表功能指定需要导出