Skip to content

Wrong serialization results while serializing object containing Map<String,Object> #229

Closed

Description

@Anand-J-Kadhi

Hello ,
I am using msgpack 0.7.0-p5 ,ObjectMapper to serialize and deserialize objects.
The problem is that i get differnent results while marshalling/unmarshalling the object.

The object of the class i am serializing is :

public class MessageData implements Serializable {

    private static final long serialVersionUID = 1L;
    private Map<String, Object> dataMap;
    private String som;
    private String accountDn;

    public Map<String, Object> getDataMap() {
        return dataMap;
    }

    public void setDataMap(Map<String, Object> dataMap) {
        this.dataMap = dataMap;
    }

    public MessageData() {

    }

    public String getSom() {
        return som;
    }

    public void setSom(String som) {
        this.som = som;
    }

    public String getAccountDn() {
        return accountDn;
    }

    public void setAccountDn(String accountDn) {
        this.accountDn = accountDn;
    }

}

when i am serializing this object the contents before serialization of the dataMap are :

{request=request, MESSAGE_REPLY_TO=null, forced=true, logger=com.sprint.vsb.common.logger.Loggers@1daf8ede, roserviceName=null, roTimer=com.sprint.vsb.common.util.TransactionTimer@3c3cfb66, serviceName=null, type=request, challengeKey=Tiq36q6cGMo54PH6, MESSAGE_JMS_CORR_ID=null, vin=BBBASDHCT92C359, correlationId=01-33-45-67-89-ab-1222-abc156, MESSAGE_TEXT={    
"som": "BBBASDHCT92C359",   "op": "doorLock",  "tp": "request",    "cid": "01-33-45-67-89-ab-1222-abc156",    "ckey": "Tiq36q6cGMo54PH6",    "ts": "1431509574287",    "fd": "true"
}, operation=doorLock, timestamp=1431509574287}

And after deserialization i get a different payload :

{request=request, MESSAGE_REPLY_TO=null, forced=true, logger={}, roserviceName=null, roTimer={count=0, snapshot={values=[], max=0, 75thPercentile=0.0, 95thPercentile=0.0, 98thPercentile=0.0, 99thPercentile=0.0, 999thPercentile=0.0, median=0.0, min=0, mean=0.0, stdDev=0.0}, meanRate=0.0, oneMinuteRate=0.0, fifteenMinuteRate=0.0, fiveMinuteRate=0.0}, serviceName=null, type=request, challengeKey=Tiq36q6cGMo54PH6, MESSAGE_JMS_CORR_ID=null, vin=BBBASDHCT92C359, correlationId=01-33-45-67-89-ab-1222-abc156, MESSAGE_TEXT={    
"som": "BBBASDHCT92C359",   "op": "doorLock",  "tp": "request",    "cid": "01-33-45-67-89-ab-1222-abc156",    "ckey": "Tiq36q6cGMo54PH6",    "ts": "1431510385988",    "fd": "true"
}, operation=doorLock, timestamp=1431510385988}

As you can see the logger element is {},roTimer is not object anymore.
How it is happening is it a BUG?

Activity

  1. komamitsu commented on May 16, 2015

    @komamitsu
    Member

    MessagePack needs to know that logger is an instance of Loggers and roTimer is an instance of TransactionTimer but Map<String, Object> doesn't include those information. One option is to create and use a POJO instead of Map<String, Object> like this.

    class DataMap {
      public String request;
      public boolean forced;
      public Loggers logger;
      public TransactionTimer roTimer;
        :
    }
    
      DataMap dataMap = objectMapper.readValue(serializedData, DataMap.class);
    
  2. Anand-J-Kadhi commented on May 21, 2015

    @Anand-J-Kadhi
    Author

    Can you suggest any other option as this would be a lot big change this time. Like the XstreamSerilaizer(xstream.codehaus.org) serializes.

  3. added this to the v07 milestone on Jun 23, 2015
  4. komamitsu commented on Aug 9, 2015

    @komamitsu
    Member

    @Anand-J-Kadhi Sorry for the late reply. How about storing Class information into serialized data like this ? You need to write serde, though.

        static class Loggers
        {
            private String name;
            private List<String> instances = new ArrayList<>();
    
            public List<String> getInstances()
            {
                return instances;
            }
    
            public void setInstances(List<String> instances)
            {
                this.instances = instances;
            }
    
            public String getName()
            {
                return name;
            }
    
            public void setName(String name)
            {
                this.name = name;
            }
        }
    
    
        @JsonSerialize(using=MessageDataSerializer.class)
        @JsonDeserialize(using=MessageDataDeserializer.class)
        static class MessageData
        {
            private String accountDn;
            private Map<String, Object> dataMap;
    
            public String getAccountDn()
            {
                return accountDn;
            }
    
            public void setAccountDn(String accountDn)
            {
                this.accountDn = accountDn;
            }
    
            public Map<String, Object> getDataMap()
            {
                return dataMap;
            }
    
            public void setDataMap(Map<String, Object> dataMap)
            {
                this.dataMap = dataMap;
            }
        }
    
        static class MessageDataSerializer
                extends JsonSerializer<MessageData>
        {
            @Override
            public void serialize(MessageData value, JsonGenerator gen, SerializerProvider serializers)
                    throws IOException, JsonProcessingException
            {
                MessageData messageData = (MessageData) value;
                gen.writeStartObject();
                gen.writeObjectField("accountDn", messageData.getAccountDn());
                HashMap<String, String> metadata = new HashMap<>();
                for (Map.Entry<String, Object> entry : messageData.getDataMap().entrySet()) {
                    metadata.put(entry.getKey(), entry.getValue().getClass().getName());
                }
                gen.writeObjectField("__metadata", metadata);
                gen.writeObjectField("dataMap", messageData.getDataMap());
                gen.writeEndObject();
            }
        }
    
        static class MessageDataDeserializer
            extends JsonDeserializer<MessageData>
        {
            @Override
            public MessageData deserialize(JsonParser p, DeserializationContext ctxt)
                    throws IOException, JsonProcessingException
            {
                MessageData messageData = new MessageData();
                TreeNode treeNode = p.readValueAsTree();
    
                TreeNode accountDn = treeNode.get("accountDn");
                messageData.setAccountDn(accountDn.traverse(p.getCodec()).readValueAs(String.class));
    
                Map<String, String> metadata = treeNode.get("__metadata").traverse(p.getCodec()).readValueAs(new TypeReference<Map<String, String>>(){});
                TreeNode dataMapTree = treeNode.get("dataMap");
                Map<String, Object> dataMap = new HashMap<>();
                for (Map.Entry<String, String> entry : metadata.entrySet()) {
                    try {
                        Object o = dataMapTree.get(entry.getKey()).traverse(p.getCodec()).readValueAs(Class.forName(entry.getValue()));
                        dataMap.put(entry.getKey(), o);
                    }
                    catch (ClassNotFoundException e) {
                        throw new RuntimeException("Failed to deserialize: " + entry, e);
                    }
                }
                messageData.setDataMap(dataMap);
    
                return messageData;
            }
        }
    
        public static void main(String[] argv)
                throws IOException
        {
            MessageData messageData = new MessageData();
            messageData.setAccountDn("1234");
            HashMap<String, Object> hashMap = new HashMap<>();
            hashMap.put("pi", 3.14);
            Loggers loggers = new Loggers();
            loggers.setName("foo loggers");
            loggers.setInstances(Arrays.asList("first", "second", "third"));
            hashMap.put("logger", loggers);
            messageData.setDataMap(hashMap);
            ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
            byte[] bytes = objectMapper.writeValueAsBytes(messageData);
            MessageData deserialized = objectMapper.readValue(bytes, MessageData.class);
            Loggers deserializedLogger = (Loggers) deserialized.getDataMap().get("logger");
    

    This way will be available in the next release.

  5. Anand-J-Kadhi commented on Aug 9, 2015

    @Anand-J-Kadhi
    Author

    Hi @komamitsu ,

    No issues , Thanks for this detailed answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions