探索 UniHttp:解锁 Xml 及 JavaBean 序列化的多种方式
目录
前言
一、使用JAXB的数据转换
1、Jaxb特性
2、实现原理
(一)序列化(Java 对象到 XML)
(二)反序列化(XML 到 Java 对象)
3、Jaxb注解简介
4、编码实现
二、基于XmlSerializeConverter的数据转换
1、XmlSerializeConverter
2、JaxbXmlSerializeConverter的具体实现
3、实现方法
三、其它第三方实现
1、实现方法
四、总结
前言
在当今数字化时代,数据的高效传输与处理已成为软件开发领域中至关重要的环节。在之前内容中介绍了天地图的行车规划接口,基于GeoTools和SpringBoot的省域驾车最快路线生成实践,也介绍了UniHttp这个工具。UniHttp,作为一种高性能的 HTTP 客户端库,凭借其强大的功能和灵活的配置,为广大开发者提供了便捷的网络通信解决方案。但是在天地图驾车导航中,其返回的数据格式是Xml的,而Xml的数据格式在应用中存在着较多的不便。
在与天地图进行交互时,路径规划请求与响应数据通常以 Xml 格式进行传输。这就需要开发者能够熟练掌握 Xml 的序列化与反序列化技术,以便准确地构建请求并解析响应数据。而 JavaBean 则以其简洁明了的结构和易于操作的特性,在 Java 开发中被广泛用于封装数据。将 Xml 数据与 JavaBean 进行相互转换,不仅可以提高代码的可读性和可维护性,还能更好地利用 Java 的面向对象特性进行数据处理。
在使用 UniHttp 进行网络请求时,如何高效地实现 Xml 和 JavaBean 的序列化与反序列化,成为了开发者需要解决的关键问题。目前,存在多种方式可以实现这一目标,每种方式都有其独特的优缺点和适用场景。通过对这些方式的深入探索与研究,我们可以更好地根据实际需求选择合适的序列化策略,从而优化网络通信过程,提高系统的整体性能。
在接下来的内容中,我们将详细探讨在 UniHttp 环境下,实现 Xml 和 JavaBean 序列化的多种方式。我们将从基础的序列化原理入手,逐步深入到各种具体实现方法,包括但不限于使用原生的 Java 库、第三方序列化工具以及自定义序列化器等。同时,结合天地图的路径规划功能,我们将通过实际案例展示如何在实际开发中应用这些序列化方式,以实现高效、准确的数据交互。通过对这些内容的深入剖析,希望能够为使用 UniHttp 进行网络开发的开发者们提供有价值的参考和指导,帮助大家更好地理解和掌握 Xml 和 JavaBean 序列化的相关技术,从而在实际项目中更加得心应手地进行开发工作。
一、使用JAXB的数据转换
JAXB(Java Architecture for XML Binding)是 Java 平台的一部分,它提供了一种便捷的方式,用于在 Java 类和 XML 表示形式之间进行数据绑定。简单来说,JAXB 能够将 Java 对象(通常是 JavaBean)转换为 XML 数据(序列化),也可以将 XML 数据转换为 Java 对象(反序列化)。
1、Jaxb特性
易用性
-
JAXB 提供了注解驱动的方式来定义 Java 类和 XML 之间的映射关系。开发者只需要在 Java 类上添加一些简单的注解,如
@XmlRootElement
、@XmlElement
等,就可以清晰地指定类的属性如何映射到 XML 元素。例如,使用@XmlRootElement
注解可以指定一个 Java 类作为 XML 文档的根元素。这种方式使得代码更加直观易懂,减少了编写繁琐的 XML 处理代码的工作量。
标准性
-
JAXB 是 Java EE(Java Platform, Enterprise Edition)的一部分,它遵循 Java 社区的标准规范。这意味着它在不同 Java 开发环境中具有良好的兼容性和一致性。开发者可以利用 JAXB 在各种 Java 应用程序中进行 XML 数据的处理,无论是 Web 应用、企业级应用还是桌面应用等。
性能
-
JAXB 在性能方面表现良好。它在序列化和反序列化过程中进行了优化,能够快速地将 Java 对象和 XML 数据进行转换。这对于处理大量数据或者对性能要求较高的应用程序来说是非常重要的。例如,在一些需要频繁与外部系统进行 XML 数据交互的企业级应用中,JAXB 能够高效地完成数据转换任务。
2、实现原理
(一)序列化(Java 对象到 XML)
-
创建 JAXBContext
-
JAXBContext 是 JAXB API 的入口点,它负责管理 Java 类和 XML 数据之间的映射信息。要进行序列化操作,首先需要创建一个 JAXBContext 实例。这通常是通过指定包含要序列化对象的 Java 类的类名来实现的。例如,
JAXBContext jaxbContext = JAXBContext.newInstance(MyJavaBeanClass.class);
,这里的MyJavaBeanClass
就是需要序列化的 Java 类。
-
-
创建 Marshaller
-
Marshaller 是用于将 Java 对象序列化为 XML 数据的类。通过 JAXBContext 的
createMarshaller()
方法可以创建一个 Marshaller 实例。然后可以设置 Marshaller 的一些属性,比如是否格式化输出 XML 数据(marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
),这可以让生成的 XML 数据更易于阅读。
-
-
序列化操作
-
最后,使用 Marshaller 的
marshal()
方法将 Java 对象序列化为 XML 数据。这个方法可以将 Java 对象写入到多种输出目标,如OutputStream
、Writer
、File
等。例如,marshaller.marshal(myJavaBeanObject, System.out);
将 Java 对象myJavaBeanObject
序列化为 XML 数据并输出到控制台。
-
(二)反序列化(XML 到 Java 对象)
-
创建 JAXBContext(与序列化相同)
-
同样地,需要创建一个 JAXBContext 实例,指定包含目标 Java 类的类名。
-
-
创建 Unmarshaller
-
Unmarshaller 是用于将 XML 数据反序列化为 Java 对象的类。通过 JAXBContext 的
createUnmarshaller()
方法可以创建一个 Unmarshaller 实例。
-
-
反序列化操作
-
使用 Unmarshaller 的
unmarshal()
方法将 XML 数据反序列化为 Java 对象。这个方法可以从多种输入源读取 XML 数据,如InputStream
、Reader
、File
等。例如,MyJavaBeanClass myJavaBeanObject = (MyJavaBeanClass) unmarshaller.unmarshal(new File("data.xml"));
将文件data.xml
中的 XML 数据反序列化为MyJavaBeanClass
类型的 Java 对象。
-
3、Jaxb注解简介
@XmlRootElement
-
用于指定一个 Java 类作为 XML 文档的根元素。例如,在
@XmlRootElement(name = "Person")
注解的 Java 类Person
中,当序列化为 XML 时,XML 文档的根元素为<Person>……</Person>
。
@XmlElement
-
用于指定 Java 类的属性或字段映射到 XML 元素。例如,
@XmlElement(name = "Name")
注解的name
字段,在序列化时会生成<Name>……</Name>
这样的 XML 元素。
@XmlAttribute
-
用于将 Java 类的属性或字段映射为 XML 元素的属性。例如,
@XmlAttribute(name = "id")
注解的id
字段,在序列化时会生成类似<Person id="123">……</Person>
的 XML 元素属性。
@XmlAccessorType
-
用于指定类成员的访问策略,决定了哪些成员(字段、属性等)会被 JAXB 处理。常见的取值有
XmlAccessType.FIELD
(处理字段)、XmlAccessType.PROPERTY
(处理属性)等。例如,@XmlAccessorType(XmlAccessType.FIELD)
表示 JAXB 会处理类的字段。
4、编码实现
首先我们看一下使用Jaxb的自助编码方式来进行Xml和JavaBean的定义实现UniHttp接口定义如下:
package com.yelang.project.thridinterface;
import com.burukeyou.uniapi.http.annotation.HttpApi;
import com.burukeyou.uniapi.http.annotation.param.QueryPar;
import com.burukeyou.uniapi.http.annotation.request.GetHttpInterface;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
@HttpApi(url = "http://api.tianditu.gov.cn/")
public interface TdtOptionService {@GetHttpInterface("drive")public HttpResponse<String> drivePlan(@QueryPar("postStr") String postStr,@QueryPar("type") String type,@QueryPar("tk") String tk);
}
请注意以上接口,驾车规划返回的对象值是:HttpResponse<String>。在调用接口之后使用编码的形式来实现自定义的转换。来看一下手动的调用服务及转换方法如下:
package com.yelang.project.unihttp;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
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;
import com.burukeyou.uniapi.annotation.UniAPIScan;
import com.burukeyou.uniapi.http.core.response.HttpResponse;
import com.google.gson.Gson;
import com.yelang.common.utils.StringUtils;
import com.yelang.project.education.domain.TdtSearchVo;
import com.yelang.project.education.domain.tdt.TdtResult;
import com.yelang.project.thridinterface.TdtDriveService;
import com.yelang.project.thridinterface.TdtOptionService;
@SpringBootTest
@RunWith(SpringRunner.class)
@UniAPIScan("com.yelang.project.thridinterface") // 指定@HttpApi接口所在的包路径
public class TdtUniHttpCase {private static final String TDT_SERVER_KEY = "tdt_key";private static final String QUERY = "query";@Autowiredprivate TdtOptionService tdtOptService;@Testpublic void xml2JavaBean() {String origInfo = "113.216171,28.190967";//黄花国际机场String destInfo = "112.957124,28.198626";//橘子洲景区// style 默认0 (0:最快路线,1:最短路线,2:避开高速,3:步行)// 这里选择避开高速String postStr = "%7B'orig':'" + origInfo + "','dest':'" + destInfo + "','style':'2'%7D" ;HttpResponse<String> resp = tdtOptService.drivePlan(postStr,"search",TDT_SERVER_KEY);try {System.out.println(resp.getBodyResult());JAXBContext context = JAXBContext.newInstance(TdtResult.class);Unmarshaller unmarshaller = context.createUnmarshaller();TdtResult result = (TdtResult) unmarshaller.unmarshal(new StringReader(resp.getBodyResult()));System.out.println("距离: " + result.getDistance());System.out.println("时长: " + result.getDuration());System.out.println("起始点: " + result.getParameters().getOrig());// 输出其他需要的数据...Gson gson = new Gson();System.out.println("json格式化如下:");System.out.println(gson.toJson(result));System.out.println(result);} catch (JAXBException e) {e.printStackTrace();}}
}
按照上面介绍的内容,我们自定义实现转换。执行完毕后可以在控制台中看到如下输出:
可以看到可以正常的输出xml结果,也可以将xml转换成javabean后进行展示。
二、基于XmlSerializeConverter的数据转换
使用上述方法对xml对象转换为JavaBean之后,虽然功能上基本是达到了我们的期望,但是有完美主义的同学一定注意到,使用这种机制不太友好,对代码的有一定的侵入性,而且要编写的代码挺多的,是否有一种机制不仅能保证对象的顺利转换,同时也能保持代码优雅性呢?本节我们就来讲讲unihttp的XmlSerializeConverter模式。
1、XmlSerializeConverter
这里我们首先来看一下UniHttp这个工具,我们在其核心注解HttpApi中可以看到以下的代码定义:
package com.burukeyou.uniapi.http.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.burukeyou.uniapi.http.core.serialize.json.FastJson2SerializeConverter;
import com.burukeyou.uniapi.http.core.serialize.json.JsonSerializeConverter;
import com.burukeyou.uniapi.http.core.serialize.xml.JaxbXmlSerializeConverter;
import com.burukeyou.uniapi.http.core.serialize.xml.XmlSerializeConverter;
import com.burukeyou.uniapi.http.extension.client.OkHttpClientFactory;
import com.burukeyou.uniapi.http.extension.processor.HttpApiProcessor;
/*** HTTP API configuration* suggest config channel-related parameters* The HTTP API annotation of UniAPI can be marked on a class or interface,* indicating that the proxy logic of HTTP API can be applied to the methods of that class or interface,* helping us quickly send and deserialize an HTTP request* @author caizhihao*/
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HttpApi {/*** Configure global HTTP request URL* Support taking values from environmental variables, such as ${xx.url}*/String url() default "";/*** Specify extension points for custom HTTP requests during execution* please see {@link HttpApiProcessor}*/Class<? extends HttpApiProcessor<? extends Annotation>> processor()[] default {};/*** Config custom http client, if not config will use default http client*/Class<? extends OkHttpClientFactory> httpClient()[] default {};/*** Config custom json converter, to serialize and deserialize http body in json*/Class<? extends JsonSerializeConverter> jsonConverter() default FastJson2SerializeConverter.class;/*** Config custom xml converter, to serialize and deserialize http body in xml*/Class<? extends XmlSerializeConverter> xmlConverter() default JaxbXmlSerializeConverter.class;;
}
在代码的最后就有一个属性,是一定自定义的xmlConverter,是一个xml的转换器,通过这个转换器就可以实现对象的高速映射。因此我们就可直接使用UniHttp自带的这个属性来进行实现。先来看看XmlSerializeConverter是什么?打开源码后发现:
package com.burukeyou.uniapi.http.core.serialize.xml;
import java.lang.reflect.Type;
import com.burukeyou.uniapi.http.core.serialize.SerializeConverter;
/*** xml** @author caizhihao*/
public interface XmlSerializeConverter extends SerializeConverter {/*** Convert the object to an XML string* @param object wait to be converted* @return XML string*/String serialize(Object object);/*** Convert the XML string to an object* @param xml wait to be converted* @param type the type of the object* @return the object*/Object deserialize(String xml, Type type);
}
你会发现这是一个xml转换的接口,核心方法就是两个,第一个是序列化节后,第二个是反序列化接口。请记住这个接口,后面在将自定义实现的时候,还会涉及这个接口。
2、JaxbXmlSerializeConverter的具体实现
在HttpApi这个类中,有一个关于xml转换器的默认实现,见如下代码:
/**
* Config custom xml converter, to serialize and deserialize http body in xml
*/
Class<? extends XmlSerializeConverter> xmlConverter() default JaxbXmlSerializeConverter.class;;
这里明确指出了其具体的实现方式,下面结合代码来讲讲具体如何进行配置。
3、实现方法
为了在httpAPI中使用xml转换器,首先在声明接口时做如下定义:
package com.yelang.project.thridinterface;
import com.burukeyou.uniapi.http.annotation.HttpApi;
import com.burukeyou.uniapi.http.annotation.param.QueryPar;
import com.burukeyou.uniapi.http.annotation.request.GetHttpInterface;
import com.burukeyou.uniapi.http.core.serialize.xml.JaxbXmlSerializeConverter;
import com.yelang.project.education.domain.tdt.TdtResult;
@HttpApi(url = "http://api.tianditu.gov.cn/",xmlConverter=JaxbXmlSerializeConverter.class)
public interface TdtDriveService {@GetHttpInterface("drive")public TdtResult drivePlan(@QueryPar("postStr") String postStr,@QueryPar("type") String type,@QueryPar("tk") String tk);
}
请注意,这里的方法返回,我们就可以直接使用一个JavaBean来接收,而不是HttpResponse对象了。调用的逻辑简单:
/**
* - testByJaxbXmlSerializeConverter 默认xml序列化实战
*/
@Test
public void testByJaxbXmlSerializeConverter() {System.out.println("testByJaxbXmlSerializeConverter进行xml转换");String origInfo = "113.216171,28.190967";//黄花国际机场String destInfo = "112.957124,28.198626";//橘子洲景区// style 默认0 (0:最快路线,1:最短路线,2:避开高速,3:步行)// 这里选择避开高速String postStr = "%7B'orig':'" + origInfo + "','dest':'" + destInfo + "','style':'2'%7D" ;TdtResult result = tdtDriveService.drivePlan(postStr,"search",TDT_SERVER_KEY);System.out.println("距离: " + result.getDistance());System.out.println("时长: " + result.getDuration());System.out.println("起始点: " + result.getParameters().getOrig());System.out.println(result);
}
在控制台中可以看到以下输出:
说明我们的程序运行正常,输出正常。
三、其它第三方实现
当然会有朋友问,如何既不想使用硬编码的方式进行实现,也不想使用默认的转换器来实现,就想自己手动发明轮子,采用完全自定义的方式来实现又该如何实现呢?本节来介绍一下如何进行完全自定义的实现。
1、实现方法
在序列化请求体和反序列化响应体默认使用的是 jaxb. 如果想要修改其他序列化方式。可以实现 XmlSerializeConverter 接口,并配置到 @HttpApi注解上。自定义XML序列化逻辑如下代码所示:
package com.yelang.project.thridinterface.converter;
import java.lang.reflect.Type;
import com.burukeyou.uniapi.http.core.serialize.xml.XmlSerializeConverter;
public class DiyXMLSerializeConverter implements XmlSerializeConverter{@Overridepublic String serialize(Object object) {return null;}@Overridepublic Object deserialize(String xml, Type type) {return null;}
}
通过这种方式自己定义相应的转换器,然后跟前面的使用的默认转换器一样,在创建HttpAPI类中进行定义和绑定,如下所示:
@HttpApi(xmlConverter = DiyXMLSerializeConverter.class)
interface UserAPI {
}
其它的参数传递和设置跟前面的内容基本一致,在此不再进行赘述。
四、总结
以上就是本文的主要内容,本文将详细探讨在 UniHttp 环境下,实现 Xml 和 JavaBean 序列化的多种方式。我们将从基础的序列化原理入手,逐步深入到各种具体实现方法,包括但不限于使用原生的 Java 库、第三方序列化工具以及自定义序列化器等。同时,结合天地图的路径规划功能,我们将通过实际案例展示如何在实际开发中应用这些序列化方式,以实现高效、准确的数据交互。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。