亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Jackson反序列化SNS消息錯誤MismatchedInputException

Jackson反序列化SNS消息錯誤MismatchedInputException

叮當貓咪 2023-09-20 15:21:37
我正在編寫一個通過 SNS HTTP 請求處理來自 Amazon Simple Email Service 的回調的功能。我想將亞馬遜提供的消息解析為本地對象結構。問題是 SNS 將 JSON 消息包裝成字符串,并且 Jackson 無法解析它。我收到錯誤:com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `xxx.email.domain.aws.ses.Notification` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"notificationType":"Delivery","mail":{"timestamp":"2019-10-02T14:43:14.570Z" ... next values of the message ... }}')來自 SNS 的整條消息如下所示: {  "Type" : "Notification",  "MessageId" : "4944xxxx-711d-57d4-91b8-8215cxxxxx",  "TopicArn" : "arn:aws:sns:eu-west-1:...",  "Message" : "{\"notificationType\":\"Delivery\",\"mail\":{\"timestamp\":\"2019-10-02T14:43:14.570Z\", ... next values of the message ... },\"delivery\":{\"timestamp\":\"2019-10-02T14:43:16.030Z\", ... next values of the message ... }}",  "Timestamp" : "2019-10-02T14:43:16.062Z",  "SignatureVersion" : "1",  "Signature" : "signature base64",  "SigningCertURL" : "cert url",  "UnsubscribeURL" : "unsubscribe url"}我的實際本地結構如下所示:@Data@JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)public class MessageWrapper {    private String type;    private String messageId;    private String topicArn;    private Notification message;    private Date timestamp;    private String signatureVersion;    private String signature;    private String signingCertURL;    private String unsubscribeURL;}@Datapublic class Notification {    private String notificationType;    private Mail mail;}@Datapublic class Mail {    private String messageId;    private String source;    private String sourceArn;    private String sourceIp;    private String sendingAccountId;    private String[] destination;}我正在尋找某種方法來告訴 JacksonMessage應該從字符串中提取并視為普通的 JSON。編輯反序列化private MessageWrapper deserializeMessage(String message) throws IOException {    return new ObjectMapper().readValue(message, MessageWrapper.class);}
查看完整描述

2 回答

?
天涯盡頭無女友

TA貢獻1831條經驗 獲得超9個贊

我認為要解決這個問題,您需要一個用于類Notification中字段的自定義反序列化器MessageWrapper以及一個用于類Mail中字段的自定義反序列化器Notification,如下所示:


public class NotificationDeserializer extends JsonDeserializer<Notification> {

    @Override

    public Notification deserialize(JsonParser p, DeserializationContext ctxt)

            throws IOException, JsonProcessingException {

        String text = p.getText();


        return new ObjectMapper().readValue(text, Notification.class);

    }

}


public class MailDeserializer extends JsonDeserializer<Mail> {

    @Override

    public Mail deserialize(JsonParser p, DeserializationContext ctxt)

            throws IOException, JsonProcessingException {

        String text = p.getText();


        return new ObjectMapper().readValue(text, Mail.class); 

    }

}

在您的類上添加一些注釋,如下所示:


@Data

@JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)

public class MessageWrapper {

    private String type;

    private String messageId;

    private String topicArn;

    @JsonDeserialize(using = NotificationDeserializer.class)

    private Notification message;

    private Date timestamp;

    private String signatureVersion;

    private String signature;

    private String signingCertURL;

    private String unsubscribeURL;

}


@Data

public class Notification {

    private String notificationType;

    @JsonDeserialize(using = MailDeserializer.class)

    private Mail mail;

}


@Data

public class Mail {

    private String messageId;

    private String source;

    private String sourceArn;

    private String sourceIp;

    private String sendingAccountId;

    private String[] destination;

}

編輯1


實際上并不MailDeserializer需要。獨自NotificationDeserializer解決這個問題。


編輯2


在自定義解串器中使用新的ObjectMapper是必須的。


查看完整回答
反對 回復 2023-09-20
?
慕雪6442864

TA貢獻1812條經驗 獲得超5個贊

messageproperty 的類型為 type Notification,但Jackson預期JSON Object不是string value。在這種情況下,您可以創建自定義反序列化器或通過某種環回實現來實現通用解決方案。如果給定的有效負載不是 aJSON Object將其讀取為 aString并使用 this 再次調用反序列化String。


為了避免StackOverflowError您需要使用另一個實例ObjectMapper或使用BeanDeserializerModifier保留BeanDeserializer實例并在遇到的地方使用它JSON Object。簡單的例子如下所示:


import com.fasterxml.jackson.core.JsonParser;

import com.fasterxml.jackson.core.JsonToken;

import com.fasterxml.jackson.databind.BeanDescription;

import com.fasterxml.jackson.databind.DeserializationConfig;

import com.fasterxml.jackson.databind.DeserializationContext;

import com.fasterxml.jackson.databind.DeserializationFeature;

import com.fasterxml.jackson.databind.JsonDeserializer;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.PropertyNamingStrategy;

import com.fasterxml.jackson.databind.annotation.JsonNaming;

import com.fasterxml.jackson.databind.deser.BeanDeserializer;

import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;

import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;

import com.fasterxml.jackson.databind.module.SimpleModule;

import com.fasterxml.jackson.databind.node.TextNode;

import lombok.Data;

import lombok.ToString;


import java.io.File;

import java.io.IOException;

import java.util.Collections;

import java.util.Date;

import java.util.Objects;

import java.util.Set;


public class JsonApp {


    public static void main(String[] args) throws Exception {

        File jsonFile = new File("./resource/test.json").getAbsoluteFile();


        SimpleModule loopBackModule = new SimpleModule();

        loopBackModule.setDeserializerModifier(new LoopBackBeanDeserializerModifier(Collections.singleton(Notification.class)));


        ObjectMapper mapper = new ObjectMapper();

        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        mapper.registerModule(loopBackModule);


        MessageWrapper wrapper = mapper.readValue(jsonFile, MessageWrapper.class);

        System.out.println(wrapper.getMessage());

    }

}


class LoopBackBeanDeserializerModifier extends BeanDeserializerModifier {


    private final Set<Class> allowedClasses;


    LoopBackBeanDeserializerModifier(Set<Class> allowedClasses) {

        this.allowedClasses = Objects.requireNonNull(allowedClasses);

    }


    @Override

    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {

        if (allowedClasses.contains(beanDesc.getBeanClass())) {

            return new LoopBackBeanDeserializer<>((BeanDeserializerBase) deserializer);

        }

        return deserializer;

    }

}


class LoopBackBeanDeserializer<T> extends BeanDeserializer {


    private final BeanDeserializerBase baseDeserializer;


    protected LoopBackBeanDeserializer(BeanDeserializerBase src) {

        super(src);

        this.baseDeserializer = src;

    }


    @Override

    public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {

        // if first token is VALUE_STRING we should read it as String and

        // run deserialization process again based on this String.

        if (p.currentToken() == JsonToken.VALUE_STRING) {

            return (T) ((ObjectMapper) p.getCodec()).readValue(p.getText(), _valueClass);

        }


        // vanilla bean deserialization

        return (T) baseDeserializer.deserialize(p, ctxt);

    }

POJO型號是一樣的。您只需要列出您期望某些問題和loop-back機制適用于它們的類。


查看完整回答
反對 回復 2023-09-20
  • 2 回答
  • 0 關注
  • 506 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號