如何编写Java自定义杰克逊解串器来解析可能为空字符串或字符串数​​组的json字段?

时间:2018-06-22 11:04:24

标签: java json jaxb json-deserialization jackson2

我需要使用REST服务提供者提供的XSD文件,使用JAXB自动生成的Java类,来管理REST API激活/返回的json结构。 由于某些原因,我不知道但我必须接受,在最新版本的XSD文件中,ENUM已在具有“值” String属性的复杂类型对象中转换,并且相关的JSON结构使用空,空String来管理这些情况。或包含定义为xsd的可接受值之一的String数组。

所以,现在我有以下xsd片段

<xsd:complexType name='SexType'>
    <xsd:sequence>
        <xsd:element name='value' minOccurs='1' maxOccurs='1'>
            <xsd:simpleType>
                <xsd:restriction base='xsd:string'>
                    <xsd:pattern value='(01|02|98){1}'/>
                </xsd:restriction>
            </xsd:simpleType>
        </xsd:element>
    </xsd:sequence>
</xsd:complexType>
<xsd:complexType name='YesNoFixedYesType'>
    <xsd:sequence>
        <xsd:element name='value' minOccurs='1' maxOccurs='1' fixed='1'>
            <xsd:simpleType>
                <xsd:restriction base='xsd:string'>
                    <xsd:pattern value='(1|0){1}'/>
                </xsd:restriction>
            </xsd:simpleType>
        </xsd:element>
    </xsd:sequence>
</xsd:complexType>
...
<xsd:element name = 'sex' minOccurs='1' maxOccurs='1' type='SexType'>
<xsd:element name = 'decision' minOccurs='1' maxOccurs='1' type='YesNoFixedYesType'>

以下JAXB生成的类

根类

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "RootClass", propOrder = {
    "decision",
    "name",
    "sex"
})
public class RootClass {
    @XmlElement(name = "decision")
    protected YesNoFixedYesType decision;
    @XmlElement(name = "name")
    protected String name;
    @XmlElement(name = "sex")
    protected SexType sex;
    [other properties ]

    [getter and setter]
}

(例如)ENUMs类

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "YesNoFixedYesType", propOrder = {
    "value"
})
@JsonDeserialize(using=GenericJsonDeserializer.class)
public class YesNoFixedYesType {

    @XmlElement(required = true)
    protected String value;

    public String getValue() {
        return value;
    }

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

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SexType", propOrder = {
    "value"
})
@JsonDeserialize(using=GenericJsonDeserializer.class)
public class SexType {

    @XmlElement(required = true)
    protected String value;

    public String getValue() {
        return value;
    }

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

}

还有我必须管理的JSON这样的声音

{
    "decision": {
        "value": [
            "1"
        ]
    },
    "name": "test",
    "sex": "",
    [other properties]
}

如您所见,JSON结构在“ decision”字段中使用String数组,在“ sex”字段中使用简单的空String。

考虑到

  • 我无法修改XSDs文件
  • 我有超过400个XSD文件,这些文件由JAXB转换为java类

我正在尝试使用

  • 对所有(ex)ENUMs类均有效的通用自定义反序列化器(之后,我还将需要自定义序列化器)
  • 已将“ JsonDeserializer”注释添加到“ SexType”和“ YesNoFixedYesType”类中(在此第一阶段,我手动将该注释添加到了类中,但是我了解到,使用jaxb绑定,应该在类生成期间由xjc添加它们过程)。

这是我的自定义反序列化器(请注意,在我的测试中,即使杰克逊规范说仅使用StdDeserializer,我也尝试扩展StdDeserializer和JsonDeserializer)

package it.ngede.impl.customizations.jackson.legapp;

import .....

public class GenericJsonDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
    private Class<?> targetClass;

    public GenericJsonDeserializer() {
        super(Object.class);
    }

    public GenericJsonDeserializer(Class<?> targetClass) {
        super(targetClass);
        this.targetClass = targetClass;
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String sourceValue = "";
        try {
            //How can I put the right value into "sourceValue" variable?

            deserializedObject = targetClass.newInstance();
            Method setterMethod = deserializedObject.getClass().getMethod("setValue", String.class);
            setterMethod.invoke(deserializedObject, sourceValue);
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
        //Now I have an instance of the annotated class I can populate the fields via reflection
        return deserializedObject;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
            BeanProperty property) throws JsonMappingException {
        //gets the class type of the annotated class
        targetClass = ctxt.getContextualType().getRawClass();
        //this new JsonApiDeserializer will be cached
        return new GenericJsonDeserializer(targetClass);
    }
}

现在,我的问题是:如何从json中提取正确的值? 我使用在互联网上找到的所有解决方案执行了许多测试,但没有一个起作用。 有没有人可以帮助我或指出我可以在哪里满足我的需求?

预先感谢

1 个答案:

答案 0 :(得分:0)

在我发布问题后几分钟(等了三天在这里写完)之后,我找到了解决方案,我认为遇到同样麻烦的人可能会有所帮助。

这是我的自定义解串器的代码

@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
    Object deserializedObject = "";
    String sourceValue = "";

    try {
        if (p.getCurrentToken() == JsonToken.START_OBJECT) {
            p.nextToken();
            if (p.getCurrentToken() == JsonToken.FIELD_NAME && "value".equalsIgnoreCase(p.getCurrentName())) {
                System.out.print(p.getCurrentName());
                p.nextToken();
                if (p.getCurrentToken() == JsonToken.START_ARRAY) {
                    boolean stillLoop = true;
                    while(stillLoop) {
                        switch (p.getCurrentToken()) {
                            case END_ARRAY:
                                stillLoop = false;
                                break;
                            case VALUE_STRING:
                                sourceValue = p.getValueAsString();
                                System.out.println(" = " + sourceValue);
                                break;
                            default:
                        }
                        p.nextToken();
                    }
                }
            }
        }

        deserializedObject = targetClass.newInstance();
        Method getterMethod = deserializedObject.getClass().getMethod("getValue");
        if (java.util.List.class.isAssignableFrom(getterMethod.getReturnType())) {
            List<String> list = (List<String>) getterMethod.invoke(deserializedObject);
            list.add(sourceValue);
        } else {
            Method setterMethod = deserializedObject.getClass().getMethod("setValue", String.class);
            setterMethod.invoke(deserializedObject, sourceValue);
        }
    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
    }
    //Now I have an instance of the annotated class I can populate the fields via reflection
    return deserializedObject;
}