Как написать собственный десериализатор jackson java для анализа поля json, которое может быть пустой строкой или массивом строк?

Мне нужно управлять структурами json, принимаемыми/возвращаемыми REST API, используя классы java, автоматически сгенерированные JAXB, с использованием файлов XSD, предоставленных поставщиком услуг REST. По какой-то причине я не знаю, но я должен принять, что в последней версии XSD-файлов ENUM были преобразованы в объекты сложного типа, которые имеют свойство String «значение», а связанные структуры JSON управляют этими случаями, используя нулевую пустую строку. или массив строк, содержащий одно из допустимых значений, определенных в xsd.

Итак, теперь у меня есть следующий фрагмент 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]
}

(ex)классы ENUM

@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 использует массив строк для поля «решение» и простую пустую строку для поля «пол».

Учитывая, что

  • Я не могу изменять файлы XSD
  • У меня есть более 400 файлов XSD, которые JAXB преобразует в классы Java.

я пытаюсь использовать

  • общий пользовательский десериализатор, действительный для всех (бывших) классов ENUM (и после того, как мне понадобится также пользовательский сериализатор)
  • Добавлена ​​аннотация "JsonDeserializer" к классам "SexType" и "YesNoFixedYesType" (на этом первом этапе я вручную добавлял эту аннотацию к классам, но понимал, что при использовании jaxb-binding их должен добавлять xjc в процессе генерации классов).

И это мой пользовательский десериализатор (обратите внимание, что во время моих тестов я пытался расширить как StdDeserializer, так и JsonDeserializer, даже если в спецификациях Джексона указано использовать только 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? Я провел много тестов, используя все решения, которые нашел в Интернете, но ни одно из них не сработало. Может ли кто-нибудь помочь мне или указать мне, где я могу найти то, что мне нужно?

заранее спасибо


person mandrake_2    schedule 22.06.2018    source источник


Ответы (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;
}
person mandrake_2    schedule 22.06.2018