最近项目中涉及到好多系统的交互, 我们是微服务boot项目,手机银行,个人网银是传统的MVC项目,通讯协议是TCP + 10位 定长XML报文。 所以我们需要把我们的Http协议 + JSON转化成为 TCP + XML 请求对方系统。
网上搜索了好多方法,都不太好用,总结下自己测试的方法。亲测可用。
一, 工具类1 如下 :
import de.odysseus.staxon.json.JsonXMLConfig; import de.odysseus.staxon.json.JsonXMLConfigBuilder; import de.odysseus.staxon.json.JsonXMLInputFactory; import de.odysseus.staxon.json.JsonXMLOutputFactory; import de.odysseus.staxon.xml.util.PrettyXMLEventWriter; import org.springframework.stereotype.Component; import javax.xml.stream.*; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; /** *** @author dcits.xupfb * @version dcits.xupfb 初始创建 */ @Component public class JsonAndXmlUtils { /** * xml转化为JSON * @param xmlString xmlString * @return String */ public static String XmlToJson(String xmlString) { StringReader input = new StringReader(xmlString); StringWriter output = new StringWriter(); JsonXMLConfig config = new JsonXMLConfigBuilder().autoArray(true).autoPrimitive(true).prettyPrint(true).build(); try { XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(input); XMLEventWriter writer = new JsonXMLOutputFactory(config ).createXMLEventWriter(output); writer.add(reader); reader.close(); writer.close(); } catch (XMLStreamException e) { e.printStackTrace(); }finally { try { output.close(); input.close(); } catch (IOException e) { e.printStackTrace(); } } return output.toString(); } /** * json转XML * * @param jsonString jsonString * @return String */ public static String JsonToXml(String jsonString){ StringReader input = new StringReader(jsonString); StringWriter output = new StringWriter(); JsonXMLConfig config = new JsonXMLConfigBuilder().multiplePI(false).repairingNamespaces(false).build(); try { XMLEventReader reader = new JsonXMLInputFactory(config).createXMLEventReader(input); XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(output); writer = new PrettyXMLEventWriter(writer); writer.add(reader); reader.close(); writer.close(); } catch (XMLStreamException e) { e.printStackTrace(); }finally { try { output.close(); input.close(); } catch (IOException e) { e.printStackTrace(); } } // remove if (output.toString().length() >= 38) { return output.toString().substring(39); } return output.toString(); } private static Pattern pattern = Pattern.compile("\\s*|\t|\r|\n"); /** * 去掉xml中的换行和空格 * * @param jsonString * @return */ public static String JsonToXmlReplaceBlank(String jsonString) { String str = JsonAndXmlUtils.JsonToXml(jsonString); String dest = ""; if (str != null) { Matcher m = pattern.matcher(str); dest = m.replaceAll(""); } return dest; } }
二, 工具类2 如下 :
import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; import java.io.ByteArrayOutputStream; import java.io.StringReader; /** ** 类功能:TODO ** @author xupfb 2020-06-11 * @desc: TODO * @modify xupfb 2020-06-11 初始创建 */ @XmlRootElement public class JaXmlUtil { /** * JavaBean转换成xml * @param obj * @return */ public static String convertToXml(Object obj) { try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK"); marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); ByteArrayOutputStream baos = new ByteArrayOutputStream(); //注意jdk版本 XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(baos, (String) marshaller.getProperty(Marshaller.JAXB_ENCODING)); xmlStreamWriter.writeStartDocument( (String) marshaller.getProperty(Marshaller.JAXB_ENCODING), "1.0"); marshaller.marshal(obj, xmlStreamWriter); xmlStreamWriter.writeEndDocument(); xmlStreamWriter.close(); return new String(baos.toString("GBK")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * xml转换成JavaBean * * @param xml * @param c * @param* @return */ @SuppressWarnings("unchecked") public static T converyToJavaBean(String xml, Class c) { T t = null; try { JAXBContext jaxbContext = JAXBContext.newInstance(c); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); t = (T) unmarshaller.unmarshal(new StringReader(xml)); } catch (Exception e) { e.printStackTrace(); } return t; }
三, JavaBean 实体类如下 :
import lombok.Data; import lombok.NoArgsConstructor; import javax.xml.bind.annotation.*; /** ** 类功能: JavaBean 和 XML 需要转换的类 ** @author xupfb 2020-06-11 * @desc: TODO * @modify xupfb 2020-06-11 初始创建 */ @Data //表示使用这个类中的 private非静态字段作为XML的序列化的属性或者元素,对应属性要使用get、set方法 @XmlAccessorType(XmlAccessType.FIELD) //xml格式数据的显示的顺序名字要和定义变量的一样,而不是@XmlElement中的name @XmlRootElement(name="Message") public class XmlAndJavaAdapterDto { @XmlElement(name="Body",required=true) private Body body; @XmlElement(name="Head",required=true) private Head head; //表示使用这个类中的 private非静态字段作为XML的序列化的属性或者元素,对应属性要使用get、set方法 @XmlAccessorType(XmlAccessType.FIELD) //xml格式数据的显示的顺序名字要和定义变量的一样,而不是@XmlElement中的name @XmlType(propOrder={"transactionId","bankId","tellerId"}) @Data @NoArgsConstructor public static class Head{ /** * 定义xml中显示的数据 */ @XmlElement(name="TransactionId",required=true) private String transactionId; @XmlElement(name="_BankId",required=true) private String bankId; @XmlElement(name="_TellerId",required=true) private String tellerId; } //表示使用这个类中的 private非静态字段作为XML的序列化的属性或者元素,对应属性要使用get、set方法 @XmlAccessorType(XmlAccessType.FIELD) //xml格式数据的显示的顺序名字要和定义变量的一样,而不是@XmlElement中的name @XmlType(propOrder={"code","name","age"}) @Data public static class Body { /** * 定义xml中显示的数据 */ @XmlElement(name="Code",required=true) private String code; @XmlElement(name="Name",required=true) private String name; @XmlElement(name="Age",required=true) private String age; } }
四, JavaBean 配合 工具类2 测试用例 如下 :
import com.dcits.branch.cloud.base.dao.entity.XmlAndJavaAdapterDto; import com.dcits.branch.cloud.base.utils.JaXmlUtil; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** ** 类功能:TODO ** @author xupfb 2020-06-04 * @desc: TODO * @modify xupfb 2020-06-04 初始创建 */ @SpringBootTest @RunWith(SpringRunner.class) @Slf4j public class XmlToBeanTest { @Test public void objectToXml(){ XmlAndJavaAdapterDto.Head head = new XmlAndJavaAdapterDto.Head(); head.setBankId("张三"); head.setTellerId("00012"); head.setTransactionId("0330012"); XmlAndJavaAdapterDto.Body dd = new XmlAndJavaAdapterDto.Body(); dd.setAge("12"); dd.setCode("q35352"); dd.setName("mynah886"); XmlAndJavaAdapterDto xmlAndJavaAdapterDto = new XmlAndJavaAdapterDto(); xmlAndJavaAdapterDto.setBody(dd); xmlAndJavaAdapterDto.setHead(head); String xml = JaXmlUtil.convertToXml(xmlAndJavaAdapterDto); log.debug("111----------------------------------"); log.debug(xml); XmlAndJavaAdapterDto xmlAndJavaAdapterDto2 = JaXmlUtil.converyToJavaBean(xml, XmlAndJavaAdapterDto.class); log.debug("222----------------------------------"); log.debug(xmlAndJavaAdapterDto2.toString()); }
四, XML报文 配合 工具类1 模拟 JavaBean和 XML转化 通讯 Socket 测试用例 如下 :
需要注意:Socket测试需要先启动- 服务端,再启动 客户端 测试。 顺序不对会报错。
4.1 测试1, 客户端启动 发送XML报文测试。
import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest @RunWith(SpringRunner.class) @Slf4j public class TestSocketClient { @Autowired private WebSocketServiceClient webSocketServiceClient; /** * 先启动服务端, 完成后 启动客户端 */ @Test public void socketTest(){ log.debug("---- 客户端开启工作 ----"); //客户端发送xml,假数据 String xml = ""; // 调用客户端 webSocketServiceClient.runSocketClient(xml); } } q35352
mynah886 12 0330012 <_BankId>张三<_TellerId>00012
4.2 测试2, 服务端启动 接受 XML报文测试。
import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** *先开启服务端, 后开启客户端 */ @SpringBootTest @RunWith(SpringRunner.class) @Slf4j public class TestSocketService { @Autowired WebSocketService webSocketService; /** * 先启动服务端,完成后启动客户端 */ @Test public void Sockets(){ log.debug("----------服务端开启----------"); //服务端收到xml后转为json后回复 webSocketService.runSocketService(); } }
4.3 服务端代码 监听模拟端口号, 接受 XML报文测试,返回响应。
import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.net.ServerSocket; import java.net.Socket; /** * socket 工具类 服务端 */ @Slf4j @Component public class WebSocketService { // 监听端口 private static final int PORT = 7777; @SneakyThrows public void runSocketService(){ ServerSocket serverSocket = null; Socket socket = null; try { //1.建立服务器的socket,并设定一个监听的端口 PORT serverSocket = new ServerSocket(PORT); //进行循环监听,获取消息的操作放在一个大循环里 while (true) { try { //2.建立跟客户端的连接 socket = serverSocket.accept(); } catch (Exception e) { log.debug("建立与客户端的连接出现异常"); } WebSocketThread webSocketThread = new WebSocketThread(socket); webSocketThread.start(); } } catch (Exception e) { log.debug("端口号被占用"); } finally { serverSocket.close(); } } }
4.3 多线程处理类。返回响应。
import com.dcits.branch.cloud.base.dao.entity.XmlAndJavaAdapterDto; import com.dcits.branch.cloud.base.utils.JaXmlUtil; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.net.Socket; /** * socket 工具类 服务端 ---- 多线程 */ @Slf4j public class WebSocketThread extends Thread { private Socket socket; InputStream inputStream; OutputStream outputStream; public WebSocketThread(Socket socket) { this.socket = socket; } @Override public void run() { try { while (true) { //接受客户端的消息并打印 log.debug("socket" + socket); //3.获取输入流 inputStream = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); //获取输出流 outputStream = socket.getOutputStream(); PrintWriter pw = new PrintWriter(outputStream); //4.读取用户输入信息 String info = null; StringBuffer b = new StringBuffer(); while ( ( info = br.readLine() ) != null ) { b.append(info); } log.debug("服务端收到消息xml---------- {}", b.toString()); //将xml转为Json //给客户一个响应 //String reply = JsonAndXmlUtils.XmlToJson(b.toString()); XmlAndJavaAdapterDto xmlAndJavaAdapterDto2 = JaXmlUtil.converyToJavaBean(b.toString(), XmlAndJavaAdapterDto.class); pw.write(xmlAndJavaAdapterDto2.toString()); //pw.write(reply); pw.flush(); //5.关闭资源 pw.close(); outputStream.close(); br.close(); inputStream.close(); socket.close(); } } catch (IOException e) { log.debug("客户端主动断开了"); } //操作结束,关闭socket try { socket.close(); } catch (IOException e) { log.debug("关闭连接出现异常"); } } }
4.4 客户端代码 监听模拟端口号,处理服务的返回的数据
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.io.*; import java.net.Socket; /** * socket 工具类 客户端 */ @Slf4j @Service public class WebSocketServiceClient { public void runSocketClient(String info) { try { final String HOST="192.168.43.216"; //1.创建一个客户端的连接 Socket socket = null; socket = new Socket(HOST,7777); //2.得到socket读写流 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); //输入流 InputStream in = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); //3.利用流程按照一定操作,对socket进行读写操作 //将xml发送给服务端 pw.write(info); pw.flush(); socket.shutdownOutput(); //接受服务器的相应 String buf = null; StringBuffer reply = new StringBuffer(256); while(!((buf = br.readLine() ) == null)){ reply.append(buf); } log.debug("第一次发送后接收到服务端消息,xml转为Json后: {}", reply.toString()); //4.关闭资源 br.close(); in.close(); pw.close(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }