【问题标题】:How to write a java custom jackson deserializer to parse json field that could be an empty string or a string array?如何编写一个 java 自定义 jackson 反序列化器来解析可能是空字符串或字符串数​​组的 json 字段?
【发布时间】:2018-06-22 11:04:24
【问题描述】:

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

所以,现在我有了下面的xsd sn-p

<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”字段使用一个字符串数组,而对“sex”字段使用一个简单的空字符串。

考虑到

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

我正在尝试使用

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

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

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 中提取正确的值? 我使用在互联网上找到的所有解决方案执行了许多测试,但没有一个有效。 有没有人可以帮助我或指出我需要什么?

提前致谢

【问题讨论】:

    标签: java json jaxb json-deserialization jackson2


    【解决方案1】:

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

    这里是我的自定义反序列化器的代码

    @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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-09
      • 2021-11-29
      • 2023-03-04
      • 1970-01-01
      相关资源
      最近更新 更多