最近對接不同系統有JSON的有xml的。百度了一番發現springboot可以直接使用jackson的jackson-dataformat-xml包來實現。
直接在pom中增加依賴
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
//不同加version 因爲jackson-bom中已經依賴了。 具體原理參看 <optional> 標籤
只需在controller中添加produces 即可定義要返回的類型
@RestController
@RequestMapping("/demo")
public class DemoController {
/**
* 統一使用XmlResponse類返回數據。
*
* @param id
* @return 定義 produces = MediaType.APPLICATION_XML_VALUE 即可將實體返回爲xml字符串
*/
@PostMapping(value = "/get", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public XmlResponse get(@RegXmlEntity User a, @RequestParam("id") String id) {
System.out.println("請求之user" + a.getUserName());
Demo demo = new Demo();
demo.setId(id);
XmlResponse xml = new XmlResponse();
Users users = new Users();
User u = new User();
u.setUserName("a");
List<User> list = new ArrayList<>();
list.add(u);
list.add(u);
users.setU(list);
return XmlResponse.ok().putOutput(users);
}
}
返回有了 還需要解析 請求參數。解析請求參數其實只需要加上 consumes 即可。但是我這裏請求參數有標準格式。爲了不影響其他的接口。所以新增一個xml參數解析器
import com.utils.XmlMapperUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
/**
* @ClassName: XmlArgumentResolver
只解析 input 節點下的xml數據
*/
public class RegXmlArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RegXmlEntity.class);//只有包含這個註解的參數才解析
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
//先獲取requestbody 注意 inputstream獲取一次之後就清空了。 將requestboddy轉成json 然後再轉成xml字符串最後轉成參數對象
String requestXml = IOUtils.toString(webRequest.getNativeRequest(HttpServletRequest.class).getInputStream(), Charset.forName("UTF-8"));
JsonNode json = XmlMapperUtils.xmlMapper.readTree(requestXml);
Object returnObj = null;
returnObj = XmlMapperUtils.xmlToBean(XmlMapperUtils.xmlMapper.writeValueAsString(json.get("input")),parameter.getParameterType());
return returnObj;
}
}
//自定義的參數註解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ClassName: RegXmlEntity
* @Description: 掛號xml實體解析註解
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RegXmlEntity {
}
附上一個 xmlMapper工具類
import com.framework.common.exception.ServiceException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
/**
* @ClassName: XmlMapperUtils
* @Description: xmlMapper工具類
*/
public class XmlMapperUtils {
public static final XmlMapper xmlMapper;
static {
xmlMapper = new XmlMapper();
SimpleModule module = new SimpleModule();
xmlMapper.registerModule(module);
//xmlMapper.setDefaultUseWrapper(false);
//反序列化時,若實體類沒有對應的屬性,是否拋出JsonMappingException異常,false忽略掉
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//字段爲null,自動忽略,不再序列化
xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//XML標籤名:使用駱駝命名的屬性名,
xmlMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
//轉化成加下劃線和大寫
xmlMapper.setPropertyNamingStrategy(new UpperCaseSnackNamingStrategy());
//設置轉換模式
xmlMapper.enable(MapperFeature.USE_STD_BEAN_NAMING);
}
/**
* @param object
* @return
* @Description 將java對象轉化爲xml字符串
* @Title beanToXml
*/
public static String beanToXml(Object object) {
try {
return xmlMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new ServiceException("Java對象轉換爲Xml失敗", e);
}
}
/**
* @param xml
* @param clazz
* @return
* @Description 將xml字符串轉化爲java對象
* @Title xmlToBean
*/
public static <T> T xmlToBean(String xml, Class<T> clazz) {
try {
return xmlMapper.readValue(xml, clazz);
} catch (IOException e) {
throw new ServiceException("Xml轉換爲Java對象失敗", e);
}
}
/**
* @author leizhigang
* @version 1.0v
* @ClassName UpperCaseSnackNamingStrategy
* @date 2020-08-04
* @description 轉大寫並加下劃線
*/
public static class UpperCaseSnackNamingStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase {
@Override
public String translate(String input) {
if (input == null) {
return input;
}
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++) {
char c = input.charAt(i);
if (i > 0 || c != '_') {
if (Character.isUpperCase(c)) {
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
result.append('_');
resultLength++;
}
wasPrevTranslated = true;
} else {
wasPrevTranslated = false;
}
c = Character.toUpperCase(c);
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
}
}
解析都有了 還需要給不同的包裏設置不同的全局異常
import com.framework.common.exception.ServiceException;
import com.regThirdPartyDocking.utils.XmlResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
/**
* 異常處理器
*
*/
@RestControllerAdvice(basePackages = "com.xml")//可以指定要鑑定的包
@Order(Ordered.HIGHEST_PRECEDENCE)
public class XrServiceExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 處理自定義異常
*/
@ExceptionHandler(ServiceException.class)
public XmlResponse handleServiceException(ServiceException e){
XmlResponse xr = new XmlResponse();
xr.put("ret_code", e.getCode());
xr.put("ret_msg", e.getMessage());
return xr;
}
@ExceptionHandler(NoHandlerFoundException.class)
public XmlResponse handlerNoFoundException(Exception e) {
logger.error(e.getMessage(), e);
return XmlResponse.error(404, "路徑不存在,請檢查路徑是否正確");
}
@ExceptionHandler(DuplicateKeyException.class)
public XmlResponse handleDuplicateKeyException(DuplicateKeyException e){
logger.error(e.getMessage(), e);
return XmlResponse.error("數據庫中已存在該記錄");
}
@ExceptionHandler(Exception.class)
public XmlResponse handleException(Exception e){
logger.error(e.getMessage(), e);
return XmlResponse.error();
}
}
ps:添加了jackson-dataformat-xml之後發現其他使用json格式的接口默認返回的都是xml格式的了(前端如果未指定application/json的話)。 網上說是xml的格式化優先級比JSON的高。按照網上的設置默認格式化方法還是不行。所以可以參考官網說明:
https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
import com..common.argumentResolver.RegXmlArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* RegWebMvc配置
*
*/
@Configuration
public class RegWebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RegXmlArgumentResolver());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).
favorParameter(true).
parameterName("mediaType").
ignoreAcceptHeader(true).
/*useJaf(false).*/
defaultContentType(MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML); //默認解析爲json
}
}