Java 使用jackson的MixIn类将JSON反序列化为JAX-B对象时出现异常

Java 使用jackson的MixIn类将JSON反序列化为JAX-B对象时出现异常,java,json,xml,jackson,Java,Json,Xml,Jackson,我们有一个简单的XSD文件。从该文件中,我们使用XJC生成Java对象。我们希望将这些对象序列化并反序列化为XML(有效)和JSON。有效的XML如下所示: <user> <loginId>demo</loginId> <roles> <role>ADMIN</role> <role>USER</role> </roles> <

我们有一个简单的XSD文件。从该文件中,我们使用XJC生成Java对象。我们希望将这些对象序列化并反序列化为XML(有效)和JSON。有效的XML如下所示:

<user>
    <loginId>demo</loginId>
    <roles>
        <role>ADMIN</role>
        <role>USER</role>
    </roles>
</user>
{
    "loginId": "demo",
    "roles": {
        "role": ["ADMIN","USER"]
    }
}
为了避免“角色”/“角色”构造,我们使用Mixin来获得以下结果:

{
    "loginId": "demo",
    "roles": ["ADMIN", "USER"]
}
这适用于序列化过程,但不适用于反序列化

下面是用于配置映射器和测试反序列化的代码:

public class JSONSupportTest {

    private static final String USER_JSON = "{\n"
        + "    \"loginId\": \"demo\",\n"
        + "    \"roles\": [\n"
        + "        \"ADMIN\",\n"
        + "        \"USER\"\n"
        + "    ]\n"
        + "}";

    @Test
    public void testJSONDeserialization() throws Exception {

        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
        mapper.setSerializationInclusion(Include.NON_NULL);    
        mapper.addMixIn(JAXBElement.class, JaxBElementMixin.class);
        mapper.addMixIn(Roles.class, UserRolesMixin.class); // this line avoids the "roles"/"role" construct

        UserType result = mapper.readValue(USER_JSON, UserType.class);
        System.out.println(result);
    }
}
使用该代码,我们将在以下异常中运行:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of de.demo.UserType$Roles out of START_ARRAY token
 at [Source: (String)"{
"loginId": "demo",
"roles": [
    "ADMIN",
    "USER"
]
}"; line: 3, column: 14] (through reference chain: de.demo.UserType["roles"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1329)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1138)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1092)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromArray(BeanDeserializerBase.java:1439)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:185)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:287)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
at de.demo.JSONSupportTest.testJSONDeserialization(JSONSupportTest.java:22)
有没有一种方式Jackson支持两种方式(序列化和反序列化)

以下是完整的代码:

XSD文件:

<schema xmlns:tns="http://demo.de" elementFormDefault="qualified"
    targetNamespace="http://demo.de" version="2.0"
    xmlns="http://www.w3.org/2001/XMLSchema">

<element name="user" type="tns:UserType"/>

<complexType name="UserType">
    <sequence>
        <element minOccurs="1" maxOccurs="1" name="loginId" type="string" />
        <element maxOccurs="1" minOccurs="1" name="roles">
            <complexType>
                <sequence>
                    <element maxOccurs="unbounded" minOccurs="1" name="role" type="tns:RoleType" />
                </sequence>
            </complexType>
        </element>
    </sequence>
</complexType>


<simpleType name="RoleType">
    <restriction base="string">
        <enumeration value="USER" />
        <enumeration value="ADMIN" />
    </restriction>
</simpleType>

XJC生成的JAX-B类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "UserType", propOrder = {
    "loginId",
    "roles"
})
public class UserType {
    @XmlElement(required = true)
    protected String loginId;
    @XmlElement(required = true)
    protected UserType.Roles roles;

    public String getLoginId() {
        return loginId;
    }

    public void setLoginId(String value) {
        this.loginId = value;
    }

    public UserType.Roles getRoles() {
        return roles;
    }

    public void setRoles(UserType.Roles value) {
        this.roles = value;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "role"
    })
    public static class Roles {

        @XmlElement(required = true)
        @XmlSchemaType(name = "string")
        protected List<RoleType> role;

        public List<RoleType> getRole() {
            if (role == null) {
                role = new ArrayList<RoleType>();
            }
            return this.role;
        }
    }
}


@XmlType(name = "RoleType")
@XmlEnum
public enum RoleType {

    USER,
    ADMIN;

    public String value() {
        return name();
    }

    public static RoleType fromValue(String v) {
        return valueOf(v);
    }
}
@xmlacessortype(xmlacesstype.FIELD)
@XmlType(name=“UserType”,propOrder={
“loginId”,
“角色”
})
公共类用户类型{
@XmlElement(必需=true)
受保护字符串loginId;
@XmlElement(必需=true)
受保护的用户类型。角色;
公共字符串getLoginId(){
返回loginId;
}
public void setLoginId(字符串值){
this.loginId=值;
}
public UserType.Roles getRoles(){
返回角色;
}
public void setRoles(UserType.Roles值){
这个角色=价值;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name=),比例={
“角色”
})
公共静态类角色{
@XmlElement(必需=true)
@XmlSchemaType(name=“string”)
受保护列表角色;
公共列表getRole(){
如果(角色==null){
role=newarraylist();
}
返回这个角色;
}
}
}
@XmlType(name=“RoleType”)
@XmlEnum
公共枚举角色类型{
用户,
管理
公共字符串值(){
返回名称();
}
公共静态角色类型fromValue(字符串v){
返回值(v);
}
}
最后是角色部分的Mixin类

public class UserRolesMixin extends Roles{

    @JsonValue
    @Override
    public List<RoleType> getRole() {
        return super.getRole();
    }
}
public类UserRolesMixin扩展了角色{
@JsonValue
@凌驾
公共列表getRole(){
返回super.getRole();
}
}

我找到了一个带有自定义反序列化器的解决方案

public class UserRolesDeserializer extends JsonDeserializer<Roles> {
    @Override
public Roles deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    TreeNode node = p.readValueAsTree();

    if (node instanceof ArrayNode) {
        ArrayNode array = (ArrayNode) node;
        Iterator<JsonNode> contentIter = array.iterator();
        Roles result = new Roles();
        while (contentIter.hasNext()) {
            JsonNode valueNode = contentIter.next();
            if (valueNode.isTextual()) {
                String roleName = valueNode.textValue();

                try {
                    RoleType role = RoleType.valueOf(roleName);
                    result.getRole().add(role);
                } catch (IllegalArgumentException e) {
                    throw new JsonParseException(p,
                        "Can't create roles for user. Unknown role found. '" + roleName + "'", e);
                }
            } else {
                throw new JsonParseException(p, "Can't create roles for user. All roles must be of type string");
            }
        }
        return result;
    } else {
        throw new JsonParseException(p,
            "Can't create roles for user. Unexpected content found. Array expected for element role");
    }

}
公共类UserRolesDeserializer扩展JsonDeserializer{
@凌驾
公共角色反序列化(JsonParser p,DeserializationContext ctxt)引发IOException,JsonProcessingException{
TreeNode节点=p.readValueAsTree();
if(ArrayNode的节点实例){
ArrayNode数组=(ArrayNode)节点;
迭代器contentIter=array.Iterator();
角色结果=新角色();
while(contentIter.hasNext()){
JsonNode valueNode=contentIter.next();
if(valueNode.isTextual()){
字符串roleName=valueNode.textValue();
试一试{
RoleType角色=RoleType.valueOf(roleName);
result.getRole().add(角色);
}捕获(IllegalArgumentException e){
抛出新的JsonParseException(p,
无法为用户创建角色。找到未知角色。“+roleName+”,e);
}
}否则{
抛出新的JsonParseException(p,“无法为用户创建角色。所有角色必须是字符串类型”);
}
}
返回结果;
}否则{
抛出新的JsonParseException(p,
“无法为用户创建角色。找到意外内容。元素角色需要数组”);
}
}
以及更新的Mixin类

@JsonDeserialize(using = UserRolesDeserializer.class)
public class UserRolesMixin extends Roles {
    @JsonValue
    @Override
    public List<RoleType> getRole() {
        return super.getRole();
    }
}
@JsonDeserialize(使用=UserRolesDeserializer.class)
公共类UserRoleMixin扩展了角色{
@JsonValue
@凌驾
公共列表getRole(){
返回super.getRole();
}
}

对我来说,这是jackson中的一个错误,它不是自动完成的,因为Mixin,应该清楚该做什么。但是出于某种原因,需要一个自定义反序列化程序。

我找到了一个带有自定义反序列化程序的解决方案

public class UserRolesDeserializer extends JsonDeserializer<Roles> {
    @Override
public Roles deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    TreeNode node = p.readValueAsTree();

    if (node instanceof ArrayNode) {
        ArrayNode array = (ArrayNode) node;
        Iterator<JsonNode> contentIter = array.iterator();
        Roles result = new Roles();
        while (contentIter.hasNext()) {
            JsonNode valueNode = contentIter.next();
            if (valueNode.isTextual()) {
                String roleName = valueNode.textValue();

                try {
                    RoleType role = RoleType.valueOf(roleName);
                    result.getRole().add(role);
                } catch (IllegalArgumentException e) {
                    throw new JsonParseException(p,
                        "Can't create roles for user. Unknown role found. '" + roleName + "'", e);
                }
            } else {
                throw new JsonParseException(p, "Can't create roles for user. All roles must be of type string");
            }
        }
        return result;
    } else {
        throw new JsonParseException(p,
            "Can't create roles for user. Unexpected content found. Array expected for element role");
    }

}
公共类UserRolesDeserializer扩展JsonDeserializer{
@凌驾
公共角色反序列化(JsonParser p,DeserializationContext ctxt)引发IOException,JsonProcessingException{
TreeNode节点=p.readValueAsTree();
if(ArrayNode的节点实例){
ArrayNode数组=(ArrayNode)节点;
迭代器contentIter=array.Iterator();
角色结果=新角色();
while(contentIter.hasNext()){
JsonNode valueNode=contentIter.next();
if(valueNode.isTextual()){
字符串roleName=valueNode.textValue();
试一试{
RoleType角色=RoleType.valueOf(roleName);
result.getRole().add(角色);
}捕获(IllegalArgumentException e){
抛出新的JsonParseException(p,
无法为用户创建角色。找到未知角色。“+roleName+”,e);
}
}否则{
抛出新的JsonParseException(p,“无法为用户创建角色。所有角色必须是字符串类型”);
}
}
返回结果;
}否则{
抛出新的JsonParseException(p,
“无法为用户创建角色。找到意外内容。元素角色需要数组”);
}
}
以及更新的Mixin类

@JsonDeserialize(using = UserRolesDeserializer.class)
public class UserRolesMixin extends Roles {
    @JsonValue
    @Override
    public List<RoleType> getRole() {
        return super.getRole();
    }
}
@JsonDeserialize(使用=UserRolesDeserializer.class)
公共类UserRoleMixin扩展了角色{
@JsonValue
@凌驾
公共列表getRole(){
返回super.getRole();
}
}
对我来说,这是jackson中的一个bug,它不是自动完成的,因为Mixin,应该很清楚该做什么。但是出于某种原因,需要一个自定义的反序列化程序