【问题标题】:Jackson xml map attribute value to propertyJackson xml将属性值映射到属性
【发布时间】:2018-09-15 20:38:39
【问题描述】:

我正在与旧系统集成,并且需要将以下 xml 解析到我的对象中。我正在尝试用杰克逊来做到这一点,但我无法让映射工作。有谁知道如何将以下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')

【问题讨论】:

  • 你能分享你的配置吗?和依赖关系?
  • @kakabali 我已将这些添加到问题中
  • 您遇到了什么错误?或者你只是得到空对象?
  • 刚刚返回空属性。所以映射不起作用

标签: java spring-boot jackson-dataformat-xml xmlmapper


【解决方案1】:

这确实有效。

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

【讨论】:

    【解决方案2】:

    我已经彻底查看了杰克逊,但似乎没有办法做到这一点。不过,我会在这里分享我的解决方案,以防它对其他人有用。

    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");
        }
    }
    

    我选择将其隐藏在消息转换器中,因此我不必再次查看它,但您可以在您认为合适的地方应用这些步骤。如果你选择这个路由,你需要配置一个rest模板来使用这个转换器。如果没有,将 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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-07
      • 1970-01-01
      • 2014-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多