背景:
(注意日期元素 - 即具有“nillable”属性 - 似乎导致“com.fasterxml.jackson.databind.JsonMappingException”)。
问题:
如何使用 com.fasterxml.jackson.databind.ObjectMapper 将 json 字符串解析为相应的 JAXB 对象形式(参见下面的示例代码)?
非常简单的模式如下所示(注意带有“nillable”属性的日期元素):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
targetNamespace="http://aaa.bbb.ccc/things"
attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:es="http://aaa.bbb.ccc/things"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="ThingType">
<xs:all>
<xs:element name="ThingNbr" type="xs:string" minOccurs="0" />
<xs:element name="ThingDt" type="xs:date" minOccurs="0" nillable="true"/>
</xs:all>
</xs:complexType>
<xs:element name="ThingList">
<xs:complexType>
<xs:sequence>
<xs:element name="Thing" type="es:ThingType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
失败的示例代码...
package aaa.bbb.ccc.jar;
import aaa.bbb.ccc.generated.ThingList;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Thingtst {
public static void main(String[] args) {
Thingtst tt = new Thingtst();
ObjectMapper om = new ObjectMapper();
om.setPropertyNamingStrategy(new PreservePropertyNameStrategy()); //not available in jackson 2.6.3: PropertyNamingStrategy.UPPER_CASE);
String jsonString = "{\"Thing\":[{\"ThingNbr\":\"33333333\",\"ThingDt\":\"2017-10-18T00:00:00.000-04:00\"}]}";
try {
ThingList tl = om.readValue(jsonString, ThingList.class); //<== causes JsonMappingException!!!
System.out.println("ThingNbr=" + tl.getThing().get(0).getThingNbr() + "...ThingDt=" + tl.getThing().get(0).getThingDt());
} catch (IOException e) {
e.printStackTrace();
}
}
}
这是相关的异常堆栈跟踪:
-
-
-
[INFO] --- exec-maven-plugin:1.2.1:exec (default-cli) @ thingtst ---
com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class javax.xml.bind.JAXBElement<javax.xml.datatype.XMLGregorianCalendar>] from String value ('2017-10-18T00:00:00.000-04:00'); no single-String constructor/factory method
at [Source: {"Thing":[{"ThingNbr":"33333333","ThingDt":"2017-10-18T00:00:00.000-04:00"}]}; line: 1, column: 33] (through reference chain: aaa.bbb.ccc.generated.ThingList["Thing"]->java.util.ArrayList[0]->aaa.bbb.ccc.generated.ThingType["ThingDt"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:878)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:281)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:284)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1176)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:145)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:136)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:258)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:217)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:258)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2726)
at aaa.bbb.ccc.jar.Thingtst.main(Thingtst.java:18) -
-
-
如有需要,提供更多信息
ThingList.java(我一直无法将 json 字符串解析成这个对象)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2017.10.12 at 06:32:00 PM EDT
//
package aaa.bbb.ccc.generated;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="Thing" type="{http://aaa.bbb.ccc/things}ThingType" maxOccurs="unbounded" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"thing"
})
@XmlRootElement(name = "ThingList")
public class ThingList {
@XmlElement(name = "Thing")
protected List<ThingType> thing;
/**
* Gets the value of the thing property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the thing property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getThing().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link ThingType }
*
*
*/
public List<ThingType> getThing() {
if (thing == null) {
thing = new ArrayList<ThingType>();
}
return this.thing;
}
}
事物类型.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.11
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2017.10.12 at 06:32:00 PM EDT
//
package aaa.bbb.ccc.generated;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;
/**
* <p>Java class for ThingType complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="ThingType">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <all>
* <element name="ThingNbr" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="ThingDt" type="{http://www.w3.org/2001/XMLSchema}date" minOccurs="0"/>
* </all>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ThingType", propOrder = {
})
public class ThingType {
@XmlElement(name = "ThingNbr")
protected String thingNbr;
@XmlElementRef(name = "ThingDt", namespace = "http://aaa.bbb.ccc/things", type = JAXBElement.class, required = false)
protected JAXBElement<XMLGregorianCalendar> thingDt;
/**
* Gets the value of the thingNbr property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getThingNbr() {
return thingNbr;
}
/**
* Sets the value of the thingNbr property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setThingNbr(String value) {
this.thingNbr = value;
}
/**
* Gets the value of the thingDt property.
*
* @return
* possible object is
* {@link JAXBElement }{@code <}{@link XMLGregorianCalendar }{@code >}
*
*/
public JAXBElement<XMLGregorianCalendar> getThingDt() {
return thingDt;
}
/**
* Sets the value of the thingDt property.
*
* @param value
* allowed object is
* {@link JAXBElement }{@code <}{@link XMLGregorianCalendar }{@code >}
*
*/
public void setThingDt(JAXBElement<XMLGregorianCalendar> value) {
this.thingDt = value;
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>aaa.bbb.ccc</groupId>
<artifactId>thingtst</artifactId>
<version>1</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>2.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>2.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-cxf</artifactId>
<version>2.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/resources/xsd/Thing.xsd</source>
</sources>
<packageName>aaa.bbb.ccc.generated</packageName>
<verbose default-value="false">${xjc.verbose}</verbose>
</configuration>
</plugin>
</plugins>
</build>
</project>
环境:
Java 1.8.x
找到修复,参考下面 Veeram 的帖子。
通过添加此依赖项来修改 pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.3</version>
</dependency>
添加 binding.ejb 文件(如下) - 位置:src/main/ejb/binding.ejb:
<jxb:bindings version="2.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<jxb:bindings schemaLocation="../resources/xsd/Thing.xsd">
<jxb:globalBindings generateElementProperty="false">
<jxb:javaType
name="java.time.ZonedDateTime"
xmlType="xs:date"
parseMethod="aaa.bbb.ccc.jar.DateTimeAdapter.parseDateTime"
printMethod="aaa.bbb.ccc.jar.DateTimeAdapter.formatDateTime" />
</jxb:globalBindings>
</jxb:bindings>
</jxb:bindings>
添加日期时间适配器:
package aaa.bbb.ccc.jar;
import java.time.ZonedDateTime;
public class DateTimeAdapter {
public static String formatDateTime(ZonedDateTime dateTime) {
return dateTime.toString();
}
public static ZonedDateTime parseDateTime(String dateTime) {
return ZonedDateTime.parse(dateTime);
}
}
Thingtst.java 现在看起来像这样:
package aaa.bbb.ccc.jar;
import aaa.bbb.ccc.generated.*;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.*;
import java.io.IOException;
public class Thingtst {
public static void main(String[] args) throws JsonMappingException {
ObjectMapper om = new ObjectMapper();
om.setPropertyNamingStrategy(new PreservePropertyNameStrategy()); //not available in jackson 2.6.3: PropertyNamingStrategy.UPPER_CASE);
om.registerModule(new JavaTimeModule());
String jsonString = "{\"Thing\":[{\"ThingNbr\":\"33333333\",\"ThingDt\":\"2017-10-18T00:00:00.000-04:00\"}]}";
try {
ThingList tl = om.readValue(jsonString, ThingList.class); //<== causes JsonMappingException!!!
System.out.println("ThingNbr=" + tl.getThing().get(0).getThingNbr() + "...ThingDt=" + tl.getThing().get(0).getThingDt());
} catch (IOException e) {
e.printStackTrace();
}
}
}
项目结构: