HornetQ - 如何发送和转换POJO

时间:2018-03-31 15:45:26

标签: jackson gson pojo hornetq

我在嵌入模式下使用HornetQ。

我正在尝试从Publisher向Consumer发送一个POJO对象:

public class Pojo implements Serializable {

    private Integer id;
    private String  name;
    private String  phone;

    // constructors, getters & setters
}

我的想法是将POJO转换为Map并通过ClientMessage发送每个属性。 (通过这种方式,消费者将能够按POJO的属性过滤消息)

为实现这一目标,我正在使用Jackson ObjectMapper。

出版商

ObjectMapper mapper = new ObjectMapper();
Map<String, Object> pojoMap = mapper.convertValue(new Pojo(13, "name", "phone"), new TypeReference<Map<String, Object>>() {});
pojoMap.forEach(message::putObjectProperty);
producer.send(message);

消费

consumer.setMessageHandler(message -> {
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    Pojo pojo = mapper.convertValue(mapJson, Pojo.class);
});

问题是在反序列化期间(在Consumer中),ObjectMapper会抛出异常:

java.lang.IllegalArgumentException: Cannot deserialize instance of 'java.lang.String' out of START_OBJECT token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: org.hornetq.core.example.Pojo["phone"])
...

根据我的理解,ObjectMapper正在寻找'phone'值并想要一个String,但它会找到一个Object并崩溃。

我该如何解决这个问题?

有替代方案吗?

我也尝试使用Gson而不是Jackson,但它给我带来了同样的错误。

有趣的事实是,如果你发送一个没有任何String参数的对象,它的工作没有任何问题。

(不是必要的,但如果你愿意的话)在这里你可以找到整个类:

1 个答案:

答案 0 :(得分:2)

这并不是很明显,但杰克逊在杰里逊不知道的org.hornetq.api.core.SimpleString反序列化时失败了。 Gson也是如此,因为SimpleString不是标准类。

这里有两个选项:

  • 将传入的消息有效负载规范化为标准对象。
  • 或实现自定义SimpleString序列化程序/反序列化程序。

选项#1:规范化

顺便说一下,Message.toMap()也会返回消息系统属性(你的PasteBin中的代码似乎使用它),因此它们可能会与你的自定义对象属性发生冲突(我不会使用HornetQ所以我可以使用错误的条款)。 我相信,属性应该像这样标准化:

public static Map<String, Object> getNormalizedPropertiesFrom(final Message message) {
    return message.getPropertyNames()
            .stream()
            .collect(Collectors.toMap(
                    SimpleString::toString,
                    simpleString -> {
                        final Object objectProperty = message.getObjectProperty(simpleString);
                        if ( objectProperty instanceof SimpleString ) {
                            return objectProperty.toString();
                        }
                        return objectProperty;
                    }
            ));
}

这与Message.toMap()不同,会丢弃系统属性,例如durableaddressmessageIDexpirationtype,{{1 }和priority。 所以,你需要的是timestamp

选项#2:自定义(反)序列化

在这种情况下,您可以简化属性提取

mapper.convertValue(getNormalizedPropertiesFrom(message), Pojo.class)

但是您的public static Map<SimpleString, Object> getPropertiesFrom(final Message message) { return message.getPropertyNames() .stream() .collect(Collectors.toMap(Function.identity(), message::getObjectProperty)); } 实例必须知道ObjectMapper是如何(de)序列化的。 请注意,SimpleString(我相信)在转换时使用中间对象,它需要针对此方案的序列化程序(ObjectMapper.convertValue() - &gt; Message通过自定义序列化 - &gt; 一些中间表示通过内置反序列化 - &gt; Map<SimpleString, Object>)。

Pojo

出于性能原因,您应该使用单个final ObjectMapper objectMapper = new ObjectMapper() .registerModule(new SimpleModule() .addSerializer(SimpleString.class, new JsonSerializer<SimpleString>() { @Override public void serialize(@Nonnull final SimpleString simpleString, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(simpleString.toString()); } }) ) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 实例。

两个选项都会产生:

  

ObjectMapper

代表Pojo{id=13, name=name, phone=phone}类,

  

Pojo

表示属性,类似

  

{phone=phone, name=name, id=13}

用于客户端消息。