杰克逊 xml 将属性值映射到 属性
Jackson xml map attribute value to property
我正在与旧系统集成,需要将以下 xml 解析到我的对象中。我正在尝试使用 jackson 执行此操作,但我无法使映射正常工作。有人知道如何将以下 xml 映射到 pojo 吗?
@JacksonXmlRootElement(localName = "properties")
@Data
public class Example {
private String token;
private String affid;
private String domain;
}
xml 示例:
<properties>
<entry key="token">rent</entry>
<entry key="affid">true</entry>
<entry key="domain">checking</entry>
</properties>
我试过添加
@JacksonXmlProperty(isAttribute = true, localName = "key")
属性,但这当然行不通,而且我看不到另一种方法可以使它起作用。有什么想法吗?
我正在这样使用映射器...
ObjectMapper xmlMapper = new XmlMapper();
dto = xmlMapper.readValue(XML_STRING, Example .class);
我正在使用以下依赖项
compile('org.springframework.boot:spring-boot-starter-web')
runtime('org.springframework.boot:spring-boot-devtools')
compileOnly('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile('org.apache.commons:commons-lang3:3.5')
compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml')
compile('com.squareup.okhttp3:okhttp:3.10.0')
Jackson我已经仔细看过了,好像没有办法做到这一点。不过,我会在这里分享我的解决方案,以防对其他人有用。
package com.example.config;
import com.example.dto.Example;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Converter extends AbstractHttpMessageConverter<Example> {
private static final XPath XPATH_INSTANCE = XPathFactory.newInstance().newXPath();
private static final StringHttpMessageConverter MESSAGE_CONVERTER = new StringHttpMessageConverter();
@Override
protected boolean supports(Class<?> aClass) {
return aClass == Example.class;
}
@Override
protected Example readInternal(Class<? extends LongFormDTO> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
String responseString = MESSAGE_CONVERTER.read(String.class, httpInputMessage);
Reader xmlInput = new StringReader(responseString);
InputSource inputSource = new InputSource(xmlInput);
Example dto = new Example();
Node xml;
try {
xml = (Node) XPATH_INSTANCE.evaluate("/properties", inputSource, XPathConstants.NODE);
} catch (XPathExpressionException e) {
log.error("Unable to parse response", e);
return dto;
}
log.info("processing populate application response={}", responseString);
dto.setToken(getString("token", xml));
dto.setAffid(getInt("affid", xml, 36));
dto.domain(getString("domain", xml));
xmlInput.close();
return dto;
}
private String getString(String propName, Node xml, String defaultValue) {
String xpath = String.format("//entry[@key='%s']/text()", propName);
try {
String value = (String) XPATH_INSTANCE.evaluate(xpath, xml, XPathConstants.STRING);
return StringUtils.isEmpty(value) ? defaultValue : value;
} catch (XPathExpressionException e) {
log.error("Received error retrieving property={} from xml", propName, e);
}
return defaultValue;
}
private String getString(String propName, Node xml) {
return getString(propName, xml, null);
}
private int getInt(String propName, Node xml, int defaultValue) {
String stringValue = getString(propName, xml);
if (!StringUtils.isEmpty(stringValue)) {
try {
return Integer.parseInt(stringValue);
} catch (NumberFormatException e) {
log.error("Attempted to parse value={} as integer but received error", stringValue, e);
}
}
return defaultValue;
}
private int getInt(String propName, Node xml) {
return getInt(propName, xml,0);
}
private boolean getBoolean(String propName, Node xml) {
String stringValue = getString(propName, xml );
return Boolean.valueOf(stringValue);
}
@Override
protected void writeInternal(Example dto, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
throw new UnsupportedOperationException("Responses of type=" + MediaType.TEXT_PLAIN_VALUE + " are not supported");
}
}
我选择将其隐藏在消息转换器中,这样我就不必再次查看它,但您可以在您认为合适的地方应用这些步骤。如果你选择这条路线,你将需要配置一个休息模板来使用这个转换器。如果不是,将 xml 缓存到 Node 对象中很重要,因为每次重新生成的成本都非常高。
package com.example.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Configuration
public class RestConfig {
@Bean
@Primary
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
@Bean
public RestTemplate restTemplateLe(RestTemplateBuilder builder) {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
ExampleConverter exampleConverter = new ExampleConverter();
exampleConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
messageConverters.add(exampleConverter);
return builder.messageConverters(messageConverters)
.requestFactory(new OkHttp3ClientHttpRequestFactory())
.build();
}
}
这确实有效。
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
public class XmlParserDemo {
public static void main(String[] args) throws IOException, XMLStreamException {
String xmlString = "<properties>\n" +
" <entry key=\"token\">rent</entry>\n" +
" <entry key=\"affid\">true</entry>\n" +
" <entry key=\"domain\">checking</entry>\n" +
"</properties>";
XMLStreamReader sr = null;
sr = XMLInputFactory.newFactory().createXMLStreamReader(new StringReader(xmlString));
sr.next();
XmlMapper mapper = new XmlMapper();
List<Entry> entries = mapper.readValue(sr, new TypeReference<List<Entry>>() {
});
sr.close();
entries.forEach(e ->
System.out.println(e.key + ":" + e.value));
}
public static class Entry {
@JacksonXmlProperty(isAttribute = true, localName = "key")
private String key;
@JacksonXmlText
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
输出为:
token:rent
affid:true
domain:checking
我正在与旧系统集成,需要将以下 xml 解析到我的对象中。我正在尝试使用 jackson 执行此操作,但我无法使映射正常工作。有人知道如何将以下 xml 映射到 pojo 吗?
@JacksonXmlRootElement(localName = "properties")
@Data
public class Example {
private String token;
private String affid;
private String domain;
}
xml 示例:
<properties>
<entry key="token">rent</entry>
<entry key="affid">true</entry>
<entry key="domain">checking</entry>
</properties>
我试过添加
@JacksonXmlProperty(isAttribute = true, localName = "key")
属性,但这当然行不通,而且我看不到另一种方法可以使它起作用。有什么想法吗?
我正在这样使用映射器...
ObjectMapper xmlMapper = new XmlMapper();
dto = xmlMapper.readValue(XML_STRING, Example .class);
我正在使用以下依赖项
compile('org.springframework.boot:spring-boot-starter-web')
runtime('org.springframework.boot:spring-boot-devtools')
compileOnly('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test')
compile('org.apache.commons:commons-lang3:3.5')
compile('com.fasterxml.jackson.dataformat:jackson-dataformat-xml')
compile('com.squareup.okhttp3:okhttp:3.10.0')
Jackson我已经仔细看过了,好像没有办法做到这一点。不过,我会在这里分享我的解决方案,以防对其他人有用。
package com.example.config;
import com.example.dto.Example;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Converter extends AbstractHttpMessageConverter<Example> {
private static final XPath XPATH_INSTANCE = XPathFactory.newInstance().newXPath();
private static final StringHttpMessageConverter MESSAGE_CONVERTER = new StringHttpMessageConverter();
@Override
protected boolean supports(Class<?> aClass) {
return aClass == Example.class;
}
@Override
protected Example readInternal(Class<? extends LongFormDTO> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
String responseString = MESSAGE_CONVERTER.read(String.class, httpInputMessage);
Reader xmlInput = new StringReader(responseString);
InputSource inputSource = new InputSource(xmlInput);
Example dto = new Example();
Node xml;
try {
xml = (Node) XPATH_INSTANCE.evaluate("/properties", inputSource, XPathConstants.NODE);
} catch (XPathExpressionException e) {
log.error("Unable to parse response", e);
return dto;
}
log.info("processing populate application response={}", responseString);
dto.setToken(getString("token", xml));
dto.setAffid(getInt("affid", xml, 36));
dto.domain(getString("domain", xml));
xmlInput.close();
return dto;
}
private String getString(String propName, Node xml, String defaultValue) {
String xpath = String.format("//entry[@key='%s']/text()", propName);
try {
String value = (String) XPATH_INSTANCE.evaluate(xpath, xml, XPathConstants.STRING);
return StringUtils.isEmpty(value) ? defaultValue : value;
} catch (XPathExpressionException e) {
log.error("Received error retrieving property={} from xml", propName, e);
}
return defaultValue;
}
private String getString(String propName, Node xml) {
return getString(propName, xml, null);
}
private int getInt(String propName, Node xml, int defaultValue) {
String stringValue = getString(propName, xml);
if (!StringUtils.isEmpty(stringValue)) {
try {
return Integer.parseInt(stringValue);
} catch (NumberFormatException e) {
log.error("Attempted to parse value={} as integer but received error", stringValue, e);
}
}
return defaultValue;
}
private int getInt(String propName, Node xml) {
return getInt(propName, xml,0);
}
private boolean getBoolean(String propName, Node xml) {
String stringValue = getString(propName, xml );
return Boolean.valueOf(stringValue);
}
@Override
protected void writeInternal(Example dto, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
throw new UnsupportedOperationException("Responses of type=" + MediaType.TEXT_PLAIN_VALUE + " are not supported");
}
}
我选择将其隐藏在消息转换器中,这样我就不必再次查看它,但您可以在您认为合适的地方应用这些步骤。如果你选择这条路线,你将需要配置一个休息模板来使用这个转换器。如果不是,将 xml 缓存到 Node 对象中很重要,因为每次重新生成的成本都非常高。
package com.example.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Configuration
public class RestConfig {
@Bean
@Primary
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
@Bean
public RestTemplate restTemplateLe(RestTemplateBuilder builder) {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
ExampleConverter exampleConverter = new ExampleConverter();
exampleConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
messageConverters.add(exampleConverter);
return builder.messageConverters(messageConverters)
.requestFactory(new OkHttp3ClientHttpRequestFactory())
.build();
}
}
这确实有效。
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
public class XmlParserDemo {
public static void main(String[] args) throws IOException, XMLStreamException {
String xmlString = "<properties>\n" +
" <entry key=\"token\">rent</entry>\n" +
" <entry key=\"affid\">true</entry>\n" +
" <entry key=\"domain\">checking</entry>\n" +
"</properties>";
XMLStreamReader sr = null;
sr = XMLInputFactory.newFactory().createXMLStreamReader(new StringReader(xmlString));
sr.next();
XmlMapper mapper = new XmlMapper();
List<Entry> entries = mapper.readValue(sr, new TypeReference<List<Entry>>() {
});
sr.close();
entries.forEach(e ->
System.out.println(e.key + ":" + e.value));
}
public static class Entry {
@JacksonXmlProperty(isAttribute = true, localName = "key")
private String key;
@JacksonXmlText
private String value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
输出为:
token:rent
affid:true
domain:checking