Procházet zdrojové kódy

add:新增Hik云台控制接口-云台转动/抵近/3D定位

miloma93@163.com před 5 měsíci
rodič
revize
f56eb71d2b

+ 33 - 19
pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.4.5</version>
+        <version>2.5.6</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
     <groupId>com.sw</groupId>
@@ -15,7 +15,6 @@
     <description>patrol_editor_sys</description>
     <properties>
         <java.version>1.8</java.version>
-        <tomcat.version>9.0.64</tomcat.version>
     </properties>
 
     <dependencies>
@@ -35,22 +34,44 @@
             <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
 
-        <!--        tomcat版本指定            -->
+        <!--xml转换-->
         <dependency>
-            <groupId>org.apache.tomcat</groupId>
-            <artifactId>tomcat-juli</artifactId>
-            <version>${tomcat.version}</version>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <version>2.12.3</version>
         </dependency>
+
+        <!--http请求-->
         <dependency>
-            <groupId>org.apache.tomcat.embed</groupId>
-            <artifactId>tomcat-embed-logging-juli</artifactId>
-            <version>8.5.2</version>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5</version>
         </dependency>
-        <!--        thymeleaf-->
         <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+            <version>4.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+            <version>4.4.1</version>
+        </dependency>
+        <!--json-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>2.0.39</version>
+        </dependency>
+        <!--xml解析-->
+        <dependency>
+            <groupId>org.jdom</groupId>
+            <artifactId>jdom</artifactId>
+            <version>2.0.2</version>
         </dependency>
+
+
+
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
@@ -127,13 +148,6 @@
             <version>1.9.6</version>
         </dependency>
 
-        <!--json处理-->
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>fastjson</artifactId>
-            <version>1.2.83</version>
-        </dependency>
-
         <!--日志框架,SpringBoot默认的日志系统logback-->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 6 - 6
src/main/java/com/sw/patroleditor/config/Swagger2.java

@@ -14,8 +14,8 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
 /**
  * swagger文档配置
  *
- * 访问地址:http://localhost:8080/swagger-ui.html
- * 优化后地址:http://localhost:8080/doc.html
+ * 访问地址:http://localhost:8081/swagger-ui.html
+ * 优化后地址:http://localhost:8081/doc.html
  *
  * 参照:https://www.jianshu.com/p/7e543f0f0bd8
  *
@@ -29,15 +29,15 @@ public class Swagger2 {
         return new Docket(DocumentationType.SWAGGER_2)
                 .apiInfo(apiInfo())
                 .select()
-                .apis(basePackage("com.sw.bird.controller"))
+                .apis(basePackage("com.sw.patroleditor.controller"))
                 .build();
     }
 
     private ApiInfo apiInfo() {
         return new ApiInfoBuilder()
-                .title("驱鸟系统")
-                .description("驱鸟系统-doc")
-                .termsOfServiceUrl("http://localhost:8090/doc.html")
+                .title("标记系统")
+                .description("标记系统-doc")
+                .termsOfServiceUrl("http://localhost:8081/doc.html")
                 .version("1.0")
                 .build();
     }

+ 34 - 0
src/main/java/com/sw/patroleditor/controller/PTZController.java

@@ -0,0 +1,34 @@
+package com.sw.patroleditor.controller;
+
+import com.sw.patroleditor.common.ResultData;
+import com.sw.patroleditor.domain.model.PTZData;
+import com.sw.patroleditor.domain.model.Position3D;
+import com.sw.patroleditor.service.PTZCtrlService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Api(tags = {"云台控制"})
+@RestController
+public class PTZController {
+
+    @Resource
+    private PTZCtrlService ptzCtrlService;
+
+    @ApiOperation(value = "云台转动/递进")
+    @PutMapping("/ptz/move")
+    public ResultData move(@RequestBody PTZData ptzData) {
+        return ptzCtrlService.continuousChanged(ptzData);
+    }
+
+
+    @ApiOperation(value = "3D定位")
+    @PutMapping("/ptz/position3D")
+    public ResultData position3D(@RequestBody Position3D position3D) {
+        return ptzCtrlService.position3DCtrl(position3D);
+    }
+}

+ 34 - 0
src/main/java/com/sw/patroleditor/domain/model/EndPoint.java

@@ -0,0 +1,34 @@
+package com.sw.patroleditor.domain.model;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+public class EndPoint {
+    @ApiModelProperty("X坐标")
+    @XmlElement(name = "positionX")
+    private Integer positionX;
+    @ApiModelProperty("Y坐标")
+    @XmlElement(name = "positionY")
+    private Integer positionY;
+
+    @XmlTransient
+    public Integer getPositionX() {
+        return positionX;
+    }
+
+    public void setPositionX(Integer positionX) {
+        this.positionX = positionX;
+    }
+
+    @XmlTransient
+    public Integer getPositionY() {
+        return positionY;
+    }
+
+    public void setPositionY(Integer positionY) {
+        this.positionY = positionY;
+    }
+
+}

+ 26 - 0
src/main/java/com/sw/patroleditor/domain/model/FocusData.java

@@ -0,0 +1,26 @@
+package com.sw.patroleditor.domain.model;
+
+import io.swagger.annotations.ApiModelProperty;
+
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+
+@XmlRootElement(name = "FocusData")
+public class FocusData {
+
+    @ApiModelProperty("聚焦")
+    @XmlElement(name = "focus")
+    private String focus;
+
+    @XmlTransient
+    public String getFocus() {
+        return focus;
+    }
+
+    public void setFocus(String focus) {
+        this.focus = focus;
+    }
+}

+ 13 - 0
src/main/java/com/sw/patroleditor/domain/model/HikResponseStatus.java

@@ -0,0 +1,13 @@
+package com.sw.patroleditor.domain.model;
+
+import lombok.Data;
+
+@Data
+public class HikResponseStatus {
+    private String requestURL;
+    private Integer statusCode;
+    private String statusString;
+    private String subStatusCode;
+    private String ResponseStatus;
+}
+

+ 51 - 0
src/main/java/com/sw/patroleditor/domain/model/PTZData.java

@@ -0,0 +1,51 @@
+package com.sw.patroleditor.domain.model;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+
+@XmlRootElement(name = "PTZData")
+public class PTZData {
+
+    @ApiModelProperty("平移方向及角度? 负值为左向平移")
+    @XmlElement(name = "pan")
+    private Integer pan;
+
+    @ApiModelProperty("倾角")
+    @XmlElement(name = "tilt")
+    private Integer tilt;
+
+    @ApiModelProperty("抵近")
+    @XmlElement(name = "zoom")
+    private Integer zoom;
+
+    @XmlTransient
+    public Integer getPan() {
+        return pan;
+    }
+
+    public void setPan(Integer pan) {
+        this.pan = pan;
+    }
+
+    @XmlTransient
+    public Integer getTilt() {
+        return tilt;
+    }
+
+    public void setTilt(Integer tilt) {
+        this.tilt = tilt;
+    }
+
+    @XmlTransient
+    public Integer getZoom() {
+        return zoom;
+    }
+
+    public void setZoom(Integer zoom) {
+        this.zoom = zoom;
+    }
+}

+ 37 - 0
src/main/java/com/sw/patroleditor/domain/model/Position3D.java

@@ -0,0 +1,37 @@
+package com.sw.patroleditor.domain.model;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+@XmlRootElement(name = "position3D")
+public class Position3D {
+
+    @ApiModelProperty("左上起始坐标")
+    @XmlElement(name = "StartPoint")
+    private StartPoint startPoint;
+    @ApiModelProperty("右下终点坐标")
+    @XmlElement(name = "EndPoint")
+    private EndPoint endPoint;
+
+    @XmlTransient
+    public StartPoint getStartPoint() {
+        return startPoint;
+    }
+
+    public void setStartPoint(StartPoint startPoint) {
+        this.startPoint = startPoint;
+    }
+
+    @XmlTransient
+    public EndPoint getEndPoint() {
+        return endPoint;
+    }
+
+    public void setEndPoint(EndPoint endPoint) {
+        this.endPoint = endPoint;
+    }
+}

+ 35 - 0
src/main/java/com/sw/patroleditor/domain/model/StartPoint.java

@@ -0,0 +1,35 @@
+package com.sw.patroleditor.domain.model;
+
+import io.swagger.annotations.ApiModelProperty;
+
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+
+public class StartPoint {
+    @ApiModelProperty("X坐标")
+    @XmlElement(name = "positionX")
+    private Integer positionX;
+    @ApiModelProperty("Y坐标")
+    @XmlElement(name = "positionY")
+    private Integer positionY;
+
+    @XmlTransient
+    public Integer getPositionX() {
+        return positionX;
+    }
+
+    public void setPositionX(Integer positionX) {
+        this.positionX = positionX;
+    }
+
+    @XmlTransient
+    public Integer getPositionY() {
+        return positionY;
+    }
+
+    public void setPositionY(Integer positionY) {
+        this.positionY = positionY;
+    }
+}

+ 2 - 0
src/main/java/com/sw/patroleditor/exception/ErrorCode.java

@@ -7,6 +7,8 @@ package com.sw.patroleditor.exception;
 public enum ErrorCode {
     SUCCESS(0, "成功"),
 
+    FAIL(1,"失败"),
+
     //    系统用户相关
     ACCOUNT_NOT_EXIST(1, "用户不存在"),
 

+ 16 - 0
src/main/java/com/sw/patroleditor/service/PTZCtrlService.java

@@ -0,0 +1,16 @@
+package com.sw.patroleditor.service;
+
+import com.sw.patroleditor.common.ResultData;
+import com.sw.patroleditor.domain.model.FocusData;
+import com.sw.patroleditor.domain.model.PTZData;
+import com.sw.patroleditor.domain.model.Position3D;
+
+public interface PTZCtrlService {
+
+
+    ResultData continuousChanged(PTZData ptzData);
+
+    ResultData focusChanged(FocusData focusData);
+
+    ResultData position3DCtrl(Position3D position);
+}

+ 92 - 0
src/main/java/com/sw/patroleditor/service/impl/PTZCtrlServiceImpl.java

@@ -0,0 +1,92 @@
+package com.sw.patroleditor.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.sw.patroleditor.common.ResultData;
+import com.sw.patroleditor.domain.model.*;
+import com.sw.patroleditor.exception.BusinessException;
+import com.sw.patroleditor.exception.ErrorCode;
+import com.sw.patroleditor.service.PTZCtrlService;
+import com.sw.patroleditor.util.HkHttpUtils;
+import com.sw.patroleditor.util.XmlUtils;
+
+import org.springframework.stereotype.Service;
+
+
+/**
+ * 云台控制接口
+ *
+ * @author machao
+ */
+@Service
+public class PTZCtrlServiceImpl implements PTZCtrlService {
+
+    HkHttpUtils httpUtils = new HkHttpUtils();
+
+    /**
+     * 云台移动
+     *
+     * @param ptzData
+     * @return
+     */
+    public ResultData continuousChanged(PTZData ptzData) {
+        //拼接请求地址
+        BodyRequestModel bodyRequestModel = new BodyRequestModel();
+        String uri = "/ISAPI/PTZCtrl/channels/" + bodyRequestModel.getChannel() + "/continuous";
+        bodyRequestModel.setUri(uri);
+        //发送指令
+        return sendCMD2Hik(ptzData, bodyRequestModel, PTZData.class);
+
+    }
+
+    /**
+     * 改变焦距
+     *
+     * @param focusData
+     * @return
+     */
+    public ResultData focusChanged(FocusData focusData) {
+        BodyRequestModel bodyRequestModel = new BodyRequestModel();
+        String uri = "/ISAPI/System/Video/inputs/channels/" + bodyRequestModel.getChannel() + "/focus";
+        bodyRequestModel.setUri(uri);
+        return sendCMD2Hik(focusData, bodyRequestModel, FocusData.class);
+
+    }
+
+
+    /**
+     * 3D定位
+     *
+     * @param position3D
+     * @return
+     */
+    public ResultData position3DCtrl(Position3D position3D) {
+        BodyRequestModel bodyRequestModel = new BodyRequestModel();
+        String uri = "/ISAPI/PTZCtrl/channels/" + bodyRequestModel.getChannel() + "/position3D";
+        bodyRequestModel.setUri(uri);
+        return sendCMD2Hik(position3D, bodyRequestModel, Position3D.class);
+    }
+
+
+    /**
+     * 利用摘要认证并按照ISAPI协议向HIK发送指令
+     *
+     * @param cmdData
+     * @param bodyRequestModel
+     * @return
+     */
+    private ResultData sendCMD2Hik(Object cmdData, BodyRequestModel bodyRequestModel, Class<?>... classT) {
+        try {
+            String xml = XmlUtils.objectToXml(cmdData, classT);
+            bodyRequestModel.setEntity(xml);
+            String hkResponse = httpUtils.doBodyPut(bodyRequestModel);
+            String jsonString = XmlUtils.xml2Json(hkResponse).get("ResponseStatus").toString();
+            HikResponseStatus responseStatus = JSONObject.parseObject(jsonString, HikResponseStatus.class);
+            if (responseStatus.getStatusCode() == 1) {
+                return ResultData.success();
+            }
+        } catch (Exception e) {
+            throw new BusinessException(ErrorCode.FAIL.getCode(), e.getMessage());
+        }
+        return ResultData.fail();
+    }
+}

+ 117 - 0
src/main/java/com/sw/patroleditor/util/HkHttpUtils.java

@@ -0,0 +1,117 @@
+package com.sw.patroleditor.util;
+
+
+import com.sw.patroleditor.domain.model.BodyRequestModel;
+import org.apache.http.HttpEntity;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.*;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+
+public class HkHttpUtils {
+    /**
+     * get请求
+     *
+     * @param config
+     * @return
+     * @throws Exception
+     */
+    public String doGet(BodyRequestModel config) throws Exception {
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        AuthScope favourite_digest_realm = new AuthScope(config.getIp(), config.getPort());
+        UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(config.getUsername(), config.getPassword());
+        credsProvider.setCredentials(favourite_digest_realm, usernamePasswordCredentials);
+        CloseableHttpClient httpclient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
+        HttpGet httpGet = new HttpGet(config.getAgreement() + "://" + config.getIp() + ":" + config.getPort() + config.getUri());
+
+        CloseableHttpResponse responseBody = httpclient.execute(httpGet);
+        HttpEntity responseEntity = responseBody.getEntity();
+        if (responseEntity != null) {
+            String response = EntityUtils.toString(responseEntity);
+            return response;
+        }
+        return null;
+    }
+
+    /**
+     * delete请求
+     *
+     * @param config
+     * @return
+     * @throws Exception
+     */
+    public String doDelete(BodyRequestModel config) throws Exception {
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        AuthScope favourite_digest_realm = new AuthScope(config.getIp(), config.getPort());
+        UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(config.getUsername(), config.getPassword());
+        credsProvider.setCredentials(favourite_digest_realm, usernamePasswordCredentials);
+        CloseableHttpClient httpclient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
+        HttpDelete httpDelete = new HttpDelete(config.getAgreement() + "://" + config.getIp() + ":" + config.getPort() + config.getUri());
+        CloseableHttpResponse responseBody = httpclient.execute(httpDelete);
+        HttpEntity responseEntity = responseBody.getEntity();
+        if (responseEntity != null) {
+            String response = EntityUtils.toString(responseEntity);
+            return response;
+        }
+        return null;
+    }
+
+    /**
+     * body方式发起post请求
+     *
+     * @param config
+     * @return
+     * @throws Exception
+     */
+    public String doBodyPost(BodyRequestModel config) throws Exception {
+        String entity = config.getEntity();
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        AuthScope favourite_digest_realm = new AuthScope(config.getIp(), config.getPort());
+        UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(config.getUsername(), config.getPassword());
+        credsProvider.setCredentials(favourite_digest_realm, usernamePasswordCredentials);
+        CloseableHttpClient httpclient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
+        HttpPost httpPost = new HttpPost(config.getAgreement() + "://" + config.getIp() + ":" + config.getPort() + config.getUri());
+        httpPost.setEntity(new StringEntity(entity, "UTF-8"));
+        CloseableHttpResponse responseBody = httpclient.execute(httpPost);
+        HttpEntity responseEntity = responseBody.getEntity();
+        if (responseEntity != null) {
+            String response = EntityUtils.toString(responseEntity);
+            return response;
+        }
+        return null;
+    }
+
+    /**
+     * body方式发起post请求
+     *
+     * @param config
+     * @return
+     * @throws Exception
+     */
+    public String doBodyPut(BodyRequestModel config) throws Exception {
+        String entity = config.getEntity();
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        AuthScope favourite_digest_realm = new AuthScope(config.getIp(), config.getPort());
+        UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(config.getUsername(), config.getPassword());
+        credsProvider.setCredentials(favourite_digest_realm, usernamePasswordCredentials);
+        CloseableHttpClient httpclient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
+        HttpPut HttpPut = new HttpPut(config.getAgreement() + "://" + config.getIp() + ":" + config.getPort() + config.getUri());
+        HttpPut.setEntity(new StringEntity(entity, "UTF-8"));
+        CloseableHttpResponse responseBody = httpclient.execute(HttpPut);
+        HttpEntity responseEntity = responseBody.getEntity();
+        if (responseEntity != null) {
+            String response = EntityUtils.toString(responseEntity);
+            return response;
+        }
+        return null;
+    }
+
+}
+
+

+ 144 - 0
src/main/java/com/sw/patroleditor/util/XmlUtils.java

@@ -0,0 +1,144 @@
+package com.sw.patroleditor.util;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+import org.jdom2.input.SAXBuilder;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import java.io.*;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * xml转换工具类
+ */
+
+@Slf4j
+public class XmlUtils {
+
+
+    /**
+     * 将对象转为对应的xml报文:可利用注解自定义节点对象名
+     *
+     * @param object 对象
+     * @param classT 类
+     * @return xml
+     * @throws JAXBException ex
+     */
+    public static String objectToXml(Object object, Class<?>... classT) throws JAXBException {
+        StringWriter writer = new StringWriter();
+        JAXBContext context = JAXBContext.newInstance(classT);
+        Marshaller marshaller = context.createMarshaller();
+        //设置编码格式
+        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
+        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);
+        marshaller.marshal(object, writer);
+        return new String(writer.getBuffer());
+    }
+
+    /**
+     * 将xml报文转换为对象:可按照自定义节点名转换属性
+     *
+     * @param clazz  clazz
+     * @param xmlStr xml
+     * @return object
+     */
+    public static Object xmlToObject(String xmlStr, Class<?>... clazz) {
+        Object xmlObject = null;
+        try {
+            JAXBContext context = JAXBContext.newInstance(clazz);
+            Unmarshaller unmarshaller = context.createUnmarshaller();
+            StringReader sr = new StringReader(xmlStr);
+            xmlObject = unmarshaller.unmarshal(sr);
+        } catch (JAXBException e) {
+            log.error("xmlStrToObject error:", e);
+        }
+        return xmlObject;
+    }
+
+
+    /**
+     * 按照对象属性默认设置节点
+     *
+     * @param bean
+     * @return
+     * @throws JsonProcessingException
+     */
+    public static String bean2Xml(Object bean) throws JsonProcessingException {
+        if (bean == null) return null;
+        XmlMapper xmlMapper = new XmlMapper();
+        // 将JavaBean对象转换为XML字符串
+        String xml = xmlMapper.writeValueAsString(bean);
+        return xml;
+    }
+
+
+    /**
+     * 按照节点默认恢复对象属性
+     *
+     * @param xmlStr
+     * @return
+     * @throws JDOMException
+     * @throws IOException
+     */
+    public static JSONObject xml2Json(String xmlStr) throws JDOMException, IOException {
+        if (StringUtils.isEmpty(xmlStr)) {
+            return null;
+        }
+        xmlStr = xmlStr.replaceAll("\\\n", "");
+        byte[] xml = xmlStr.getBytes("UTF-8");
+        JSONObject json = new JSONObject();
+        InputStream is = new ByteArrayInputStream(xml);
+        SAXBuilder sb = new SAXBuilder();
+        Document doc = sb.build(is);
+        Element root = doc.getRootElement();
+        json.put(root.getName(), iterateElement(root));
+
+        return json;
+    }
+
+    private static JSONObject iterateElement(Element element) {
+        List<Element> node = element.getChildren();
+        JSONObject obj = new JSONObject();
+        List list = null;
+        for (Element child : node) {
+            list = new LinkedList();
+            String text = child.getTextTrim();
+            if (StringUtils.isBlank(text)) {
+                if (child.getChildren().size() == 0) {
+                    continue;
+                }
+                if (obj.containsKey(child.getName())) {
+                    list = (List) obj.get(child.getName());
+                }
+                list.add(iterateElement(child)); //遍历child的子节点
+                obj.put(child.getName(), list);
+            } else {
+                if (obj.containsKey(child.getName())) {
+                    Object value = obj.get(child.getName());
+                    try {
+                        list = (List) value;
+                    } catch (ClassCastException e) {
+                        list.add(value);
+                    }
+                }
+                if (child.getChildren().size() == 0) { //child无子节点时直接设置text
+                    obj.put(child.getName(), text);
+                } else {
+                    list.add(text);
+                    obj.put(child.getName(), list);
+                }
+            }
+        }
+        return obj;
+    }
+}