Skip to content

Commit 45f0cfa

Browse files
authored
Merge pull request #535 from msgpack/exttype-in-map
Support ext type key in Map
2 parents 5c2be3a + edd0942 commit 45f0cfa

File tree

3 files changed

+379
-16
lines changed

3 files changed

+379
-16
lines changed

msgpack-jackson/README.md

Lines changed: 150 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,16 +207,15 @@ When you want to use non-String value as a key of Map, use `MessagePackKeySerial
207207
@JsonSerialize(keyUsing = MessagePackKeySerializer.class)
208208
private Map<Integer, String> intMap = new HashMap<>();
209209

210-
:
211-
{
212-
intMap.put(42, "Hello");
210+
:
213211

214-
ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
215-
byte[] bytes = objectMapper.writeValueAsBytes(intMap);
212+
intMap.put(42, "Hello");
216213

217-
Map<Integer, String> deserialized = objectMapper.readValue(bytes, new TypeReference<Map<Integer, String>>() {});
218-
System.out.println(deserialized); // => {42=Hello}
219-
}
214+
ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
215+
byte[] bytes = objectMapper.writeValueAsBytes(intMap);
216+
217+
Map<Integer, String> deserialized = objectMapper.readValue(bytes, new TypeReference<Map<Integer, String>>() {});
218+
System.out.println(deserialized); // => {42=Hello}
220219
```
221220

222221
### Deserialize extension types with ExtensionTypeCustomDeserializers
@@ -316,6 +315,149 @@ When you want to use non-String value as a key of Map, use `MessagePackKeySerial
316315
// => Java
317316
```
318317

318+
#### Use extension type as Map key
319+
320+
```java
321+
static class TripleBytesPojo
322+
{
323+
public byte first;
324+
public byte second;
325+
public byte third;
326+
327+
public TripleBytesPojo(byte first, byte second, byte third)
328+
{
329+
this.first = first;
330+
this.second = second;
331+
this.third = third;
332+
}
333+
334+
@Override
335+
public boolean equals(Object o)
336+
{
337+
:
338+
}
339+
340+
@Override
341+
public int hashCode()
342+
{
343+
:
344+
}
345+
346+
@Override
347+
public String toString()
348+
{
349+
// This key format is used when serialized as map key
350+
return String.format("%d-%d-%d", first, second, third);
351+
}
352+
353+
static class KeyDeserializer
354+
extends com.fasterxml.jackson.databind.KeyDeserializer
355+
{
356+
@Override
357+
public Object deserializeKey(String key, DeserializationContext ctxt)
358+
throws IOException
359+
{
360+
String[] values = key.split("-");
361+
return new TripleBytesPojo(Byte.parseByte(values[0]), Byte.parseByte(values[1]), Byte.parseByte(values[2]));
362+
}
363+
}
364+
365+
static TripleBytesPojo deserialize(byte[] bytes)
366+
{
367+
return new TripleBytesPojo(bytes[0], bytes[1], bytes[2]);
368+
}
369+
}
370+
371+
:
372+
373+
byte extTypeCode = 42;
374+
375+
ExtensionTypeCustomDeserializers extTypeCustomDesers = new ExtensionTypeCustomDeserializers();
376+
extTypeCustomDesers.addCustomDeser(extTypeCode, new ExtensionTypeCustomDeserializers.Deser()
377+
{
378+
@Override
379+
public Object deserialize(byte[] value)
380+
throws IOException
381+
{
382+
return TripleBytesPojo.deserialize(value);
383+
}
384+
});
385+
386+
SimpleModule module = new SimpleModule();
387+
module.addKeyDeserializer(TripleBytesPojo.class, new TripleBytesPojo.KeyDeserializer());
388+
ObjectMapper objectMapper = new ObjectMapper(
389+
new MessagePackFactory().setExtTypeCustomDesers(extTypeCustomDesers))
390+
.registerModule(module);
391+
392+
Map<TripleBytesPojo, Integer> deserializedMap =
393+
objectMapper.readValue(serializedData,
394+
new TypeReference<Map<TripleBytesPojo, Integer>>() {});
395+
```
396+
397+
#### Use extension type as Map value
398+
399+
```java
400+
static class TripleBytesPojo
401+
{
402+
public byte first;
403+
public byte second;
404+
public byte third;
405+
406+
public TripleBytesPojo(byte first, byte second, byte third)
407+
{
408+
this.first = first;
409+
this.second = second;
410+
this.third = third;
411+
}
412+
413+
static class Deserializer
414+
extends StdDeserializer<TripleBytesPojo>
415+
{
416+
protected Deserializer()
417+
{
418+
super(TripleBytesPojo.class);
419+
}
420+
421+
@Override
422+
public TripleBytesPojo deserialize(JsonParser p, DeserializationContext ctxt)
423+
throws IOException, JsonProcessingException
424+
{
425+
return TripleBytesPojo.deserialize(p.getBinaryValue());
426+
}
427+
}
428+
429+
static TripleBytesPojo deserialize(byte[] bytes)
430+
{
431+
return new TripleBytesPojo(bytes[0], bytes[1], bytes[2]);
432+
}
433+
}
434+
435+
:
436+
437+
byte extTypeCode = 42;
438+
439+
ExtensionTypeCustomDeserializers extTypeCustomDesers = new ExtensionTypeCustomDeserializers();
440+
extTypeCustomDesers.addCustomDeser(extTypeCode, new ExtensionTypeCustomDeserializers.Deser()
441+
{
442+
@Override
443+
public Object deserialize(byte[] value)
444+
throws IOException
445+
{
446+
return TripleBytesPojo.deserialize(value);
447+
}
448+
});
449+
450+
SimpleModule module = new SimpleModule();
451+
module.addDeserializer(TripleBytesPojo.class, new TripleBytesPojo.Deserializer());
452+
ObjectMapper objectMapper = new ObjectMapper(
453+
new MessagePackFactory().setExtTypeCustomDesers(extTypeCustomDesers))
454+
.registerModule(module);
455+
456+
Map<String, TripleBytesPojo> deserializedMap =
457+
objectMapper.readValue(serializedData,
458+
new TypeReference<Map<String, TripleBytesPojo>>() {});
459+
```
460+
319461
### Serialize a nested object that also serializes
320462

321463
When you serialize an object that has a nested object also serializing with ObjectMapper and MessagePackFactory like the following code, it throws NullPointerException since the nested MessagePackFactory modifies a shared state stored in ThreadLocal.

msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,13 @@ public JsonToken nextToken()
341341
type = Type.EXT;
342342
ExtensionTypeHeader header = messageUnpacker.unpackExtensionTypeHeader();
343343
extensionTypeValue = new MessagePackExtensionType(header.getType(), messageUnpacker.readPayload(header.getLength()));
344-
nextToken = JsonToken.VALUE_EMBEDDED_OBJECT;
344+
if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
345+
parsingContext.setCurrentName(deserializedExtensionTypeValue().toString());
346+
nextToken = JsonToken.FIELD_NAME;
347+
}
348+
else {
349+
nextToken = JsonToken.VALUE_EMBEDDED_OBJECT;
350+
}
345351
break;
346352
default:
347353
throw new IllegalStateException("Shouldn't reach here");
@@ -391,6 +397,8 @@ public String getText()
391397
return String.valueOf(doubleValue);
392398
case BIG_INT:
393399
return String.valueOf(biValue);
400+
case EXT:
401+
return deserializedExtensionTypeValue().toString();
394402
default:
395403
throw new IllegalStateException("Invalid type=" + type);
396404
}
@@ -432,6 +440,8 @@ public byte[] getBinaryValue(Base64Variant b64variant)
432440
return bytesValue;
433441
case STRING:
434442
return stringValue.getBytes(MessagePack.UTF8);
443+
case EXT:
444+
return extensionTypeValue.getData();
435445
default:
436446
throw new IllegalStateException("Invalid type=" + type);
437447
}
@@ -563,6 +573,18 @@ public BigDecimal getDecimalValue()
563573
}
564574
}
565575

576+
private Object deserializedExtensionTypeValue()
577+
throws IOException
578+
{
579+
if (extTypeCustomDesers != null) {
580+
ExtensionTypeCustomDeserializers.Deser deser = extTypeCustomDesers.getDeser(extensionTypeValue.getType());
581+
if (deser != null) {
582+
return deser.deserialize(extensionTypeValue.getData());
583+
}
584+
}
585+
return extensionTypeValue;
586+
}
587+
566588
@Override
567589
public Object getEmbeddedObject()
568590
throws IOException, JsonParseException
@@ -571,13 +593,7 @@ public Object getEmbeddedObject()
571593
case BYTES:
572594
return bytesValue;
573595
case EXT:
574-
if (extTypeCustomDesers != null) {
575-
ExtensionTypeCustomDeserializers.Deser deser = extTypeCustomDesers.getDeser(extensionTypeValue.getType());
576-
if (deser != null) {
577-
return deser.deserialize(extensionTypeValue.getData());
578-
}
579-
}
580-
return extensionTypeValue;
596+
return deserializedExtensionTypeValue();
581597
default:
582598
throw new IllegalStateException("Invalid type=" + type);
583599
}

0 commit comments

Comments
 (0)