作者:JmCui

http://

cnblogs。com/jmcui/p/829

8823。html

一、前言

為什麼會產生這個需求呢?

我們公司作為乙方,老是被客戶追著要一份API文件,當我們把一個 Swagger 文件地址丟給客戶的時候。客戶還是很不滿意,嫌不夠正式!!死活堅持要一份 word 文件 。然後領導給了個介面模板,就把這個活交給我了……我去,近10個微服務,幾百個介面,這不得要了我的命啊(最後整理出來將近200頁的 word 文件)。最後,還是領導有辦法:要不我們把Swagger的 json檔案轉成word文件吧!

一直堅持一句話。作為使用者,人要遷就機器;作為開發者,要機器遷就人。

二、思路

領導提供了一個介面模板,類似下面這樣,其實就是一個word的table頁。想到 html 可以轉 word ,那麼問題就變成了 :

解析JSON 檔案

把JSON檔案的內容填充進html 的Table中

由html直接轉成word

幾百個介面,一氣呵成!如下,還有一個簡單的示例,就是請求引數 和 返回值 。怎麼處理呢?在程式中寫了 HTTP 的請求,封裝了需要的引數去執行了一個請求,得到相應的返回值!

Swagger文件轉Word 文件

三、實現

1、封裝物件

按照面向物件的思想,一個介面Table就是一個物件,可變的請求引數和返回引數也封裝成一個物件……

Swagger文件轉Word 文件

Table

public

class

Table

{

Java

/**

* 大標題

*/

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

}

}

Request

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

}

}

Response

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

}

}

2、解析 json

先來看看Swagger

json檔案

的格式吧!需要注意的是這個 json 檔案預設的 host 是沒有加 http:// 字首的,需要我們手動加上,因為程式的HTTP請求不像瀏覽器一樣會自動補上 http:// 的字首 ……

Swagger文件轉Word 文件

解析JSO

N真是一件枯燥的工作,大家可以按照自己想要生成模板的樣子修改這邊的程式碼……需要提的是,這裡有一點讓我糾結了好久。怎麼偽造介面的請求引數傳送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

“&”

);

}

}

3、html 模板

我們需要一個和 Word Table 模板一樣的HTML 頁面,然後利用JSP的 foreach 遍歷後臺得到的

List

集合,一氣呵成,生成所有介面……

<%——

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

>

4、效果

把程式碼執行起來後,訪問JSP頁面,不會像平常一樣看到 HTML 頁面,而是直接下載生成一個 檔案,按照SpringMVC

請求方法

命名(這個專案中是getWord檔案)。把這個檔案的字尾名改成 。doc 就能看到效果了!差不多是如下效果:

Swagger文件轉Word 文件

當然,剩下的工作,就要我們手動去整理維護了。比如:把屬於同一個類的請求分類整理到一起;把HTTP請求錯誤的返回值刪除(還無法適配所有的HTTP請求情況);整理維護效果如下:

Swagger文件轉Word 文件

swagger四、使用

如果直接採用我的API文件模板的話,只需要將 resources 目錄下的 data。json 檔案的內容替換成自己的Swagger Json 檔案內容就好。但是,考慮到我們模板中的返回引數是我們公司一個自定義的物件,所以可能這裡還需要大家根據自己的要求稍作修改,主要 修改TableServiceImpl 類下的 listResponse() 方法。

需要說明的是,這個專案還沒有很好的支援所有的HTTP請求,比如 restful 服務將請求引數放在請求路徑中的;比如引數是放在header中的;以及一系列可能沒有考慮到的bug……

另外,我覺得 TableServiceImpl 還有很大可以改善的地方,程式碼略顯冗餘。之後慢慢維護吧!當然,很歡迎大家一起來開發…哈哈

五、結語

一直覺得,IT最迷人的地方就是開源和分享,大家互不相識,即使沒有利益可圖,卻能為同一個專案,相同的目標 貢獻自己的時間和精力。想想就不可思議。寫這個博文的目地更多是分享自己的創意和想法,說實話,程式碼可能寫的有點爛。還請大家不要嫌棄,不吝指教!

六、更新說明

之前看《

Spring In Action

》的時候,發現了 RestTemplate 這個東西, 作為取代 HttpClients 的請求方式。當時就在想,要是放在這個專案中不是恰到好處?

更新說明如下:

1、引入了Spring的RestTemplate取代 HttpClients 以支援更多的Restful請求。

2、命名規範以及增加異常處理,對於無法處理的HTTP請求返回空字串。

3、修改之前匯入

data。josn

的方式,變成restTemplate。getForObject(“SwaggerJson的url地址”,Map。class);的動態獲取方式。

現在的使用方式也更加簡單:

1、修改resources目錄下

resources。properties

檔案的 swaggerUrl 為Swagger Json資源的url地址。

2、服務啟動後:訪問

http://

host

(主機):port(埠)/getWord,etc:http://localhost:8080/getWord

3、將生成的getWord檔案,增加字尾名 getWord。doc 。

GitHub 地址

https://

github。com/JMCuixy/swag

ger2word

推薦閱讀

1.

SpringBoot 整合篇

2. 手寫一套迷你版HTTP伺服器

3.

記住:永遠不要在MySQL中使用UTF-8

4. Springboot啟動原理解析

看完本文有收穫?請轉發分享給更多人