diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java index bb5064f1..dbd2a465 100644 --- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java +++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java @@ -39,6 +39,7 @@ public class MessagePackFactory private final MessagePack.PackerConfig packerConfig; private boolean reuseResourceInGenerator = true; private boolean reuseResourceInParser = true; + private boolean supportIntegerKeys = false; private ExtensionTypeCustomDeserializers extTypeCustomDesers; public MessagePackFactory() @@ -74,6 +75,12 @@ public MessagePackFactory setReuseResourceInParser(boolean reuseResourceInParser return this; } + public MessagePackFactory setSupportIntegerKeys(boolean supportIntegerKeys) + { + this.supportIntegerKeys = supportIntegerKeys; + return this; + } + public MessagePackFactory setExtTypeCustomDesers(ExtensionTypeCustomDeserializers extTypeCustomDesers) { this.extTypeCustomDesers = extTypeCustomDesers; @@ -84,7 +91,7 @@ public MessagePackFactory setExtTypeCustomDesers(ExtensionTypeCustomDeserializer public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { - return new MessagePackGenerator(_generatorFeatures, _objectCodec, out, packerConfig, reuseResourceInGenerator); + return new MessagePackGenerator(_generatorFeatures, _objectCodec, out, packerConfig, reuseResourceInGenerator, supportIntegerKeys); } @Override diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java index 11aba6e5..abb5089e 100644 --- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java +++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java @@ -50,6 +50,7 @@ public class MessagePackGenerator private static final ThreadLocal messageBufferOutputHolder = new ThreadLocal<>(); private final OutputStream output; private final MessagePack.PackerConfig packerConfig; + private final boolean supportIntegerKeys; private int currentParentElementIndex = -1; private int currentState = IN_ROOT; @@ -188,13 +189,15 @@ private MessagePackGenerator( int features, ObjectCodec codec, OutputStream out, - MessagePack.PackerConfig packerConfig) + MessagePack.PackerConfig packerConfig, + boolean supportIntegerKeys) { super(features, codec); this.output = out; this.messagePacker = packerConfig.newPacker(out); this.packerConfig = packerConfig; this.nodes = new ArrayList<>(); + this.supportIntegerKeys = supportIntegerKeys; } public MessagePackGenerator( @@ -202,7 +205,8 @@ public MessagePackGenerator( ObjectCodec codec, OutputStream out, MessagePack.PackerConfig packerConfig, - boolean reuseResourceInGenerator) + boolean reuseResourceInGenerator, + boolean supportIntegerKeys) throws IOException { super(features, codec); @@ -210,6 +214,7 @@ public MessagePackGenerator( this.messagePacker = packerConfig.newPacker(getMessageBufferOutputForOutputStream(out, reuseResourceInGenerator)); this.packerConfig = packerConfig; this.nodes = new ArrayList<>(); + this.supportIntegerKeys = supportIntegerKeys; } private MessageBufferOutput getMessageBufferOutputForOutputStream( @@ -373,7 +378,7 @@ else if (v instanceof MessagePackExtensionType) { else { messagePacker.flush(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - MessagePackGenerator messagePackGenerator = new MessagePackGenerator(getFeatureMask(), getCodec(), outputStream, packerConfig); + MessagePackGenerator messagePackGenerator = new MessagePackGenerator(getFeatureMask(), getCodec(), outputStream, packerConfig, supportIntegerKeys); getCodec().writeValue(messagePackGenerator, v); output.write(outputStream.toByteArray()); } @@ -513,6 +518,17 @@ private void writeByteArrayTextKey(byte[] text, int offset, int len) throws IOEx addValueNode(new String(text, offset, len, DEFAULT_CHARSET)); } + @Override + public void writeFieldId(long id) throws IOException + { + if (this.supportIntegerKeys) { + addKeyNode(id); + } + else { + super.writeFieldId(id); + } + } + @Override public void writeFieldName(String name) { diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java index 4876c797..e59b7f57 100644 --- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java +++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java @@ -621,6 +621,11 @@ public String currentName() return streamReadContext.getCurrentName(); } + public boolean isCurrentFieldId() + { + return this.type == Type.INT || this.type == Type.LONG; + } + @Override public String getCurrentName() throws IOException diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForFieldIdTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForFieldIdTest.java new file mode 100644 index 00000000..0e102ba8 --- /dev/null +++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForFieldIdTest.java @@ -0,0 +1,132 @@ +// +// MessagePack for Java +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +package org.msgpack.jackson.dataformat; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.NullValueProvider; +import com.fasterxml.jackson.databind.deser.impl.JDKValueInstantiators; +import com.fasterxml.jackson.databind.deser.std.MapDeserializer; +import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.LinkedHashMap; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MessagePackDataformatForFieldIdTest +{ + static class MessagePackMapDeserializer extends MapDeserializer + { + public static KeyDeserializer keyDeserializer = new KeyDeserializer() + { + @Override + public Object deserializeKey(String s, DeserializationContext deserializationContext) + throws IOException + { + JsonParser parser = deserializationContext.getParser(); + if (parser instanceof MessagePackParser) { + MessagePackParser p = (MessagePackParser) parser; + if (p.isCurrentFieldId()) { + return Integer.valueOf(s); + } + } + return s; + } + }; + + public MessagePackMapDeserializer() + { + super( + TypeFactory.defaultInstance().constructMapType(Map.class, Object.class, Object.class), + JDKValueInstantiators.findStdValueInstantiator(null, LinkedHashMap.class), + keyDeserializer, null, null); + } + + public MessagePackMapDeserializer(MapDeserializer src, KeyDeserializer keyDeser, + JsonDeserializer valueDeser, TypeDeserializer valueTypeDeser, NullValueProvider nuller, + Set ignorable, Set includable) + { + super(src, keyDeser, valueDeser, valueTypeDeser, nuller, ignorable, includable); + } + + @Override + protected MapDeserializer withResolved(KeyDeserializer keyDeser, TypeDeserializer valueTypeDeser, + JsonDeserializer valueDeser, NullValueProvider nuller, Set ignorable, + Set includable) + { + return new MessagePackMapDeserializer(this, keyDeser, (JsonDeserializer) valueDeser, valueTypeDeser, + nuller, ignorable, includable); + } + } + + @Test + public void testMixedKeys() + throws IOException + { + ObjectMapper mapper = new ObjectMapper( + new MessagePackFactory() + .setSupportIntegerKeys(true) + ) + .registerModule(new SimpleModule() + .addDeserializer(Map.class, new MessagePackMapDeserializer())); + + Map map = new HashMap<>(); + map.put(1, "one"); + map.put("2", "two"); + + byte[] bytes = mapper.writeValueAsBytes(map); + Map deserializedInit = mapper.readValue(bytes, new TypeReference>() {}); + + Map expected = new HashMap<>(map); + Map actual = new HashMap<>(deserializedInit); + + assertEquals(expected, actual); + } + + @Test + public void testMixedKeysBackwardsCompatiable() + throws IOException + { + ObjectMapper mapper = new ObjectMapper(new MessagePackFactory()) + .registerModule(new SimpleModule() + .addDeserializer(Map.class, new MessagePackMapDeserializer())); + + Map map = new HashMap<>(); + map.put(1, "one"); + map.put("2", "two"); + + byte[] bytes = mapper.writeValueAsBytes(map); + Map deserializedInit = mapper.readValue(bytes, new TypeReference>() {}); + + Map expected = new HashMap<>(); + expected.put("1", "one"); + expected.put("2", "two"); + Map actual = new HashMap<>(deserializedInit); + + assertEquals(expected, actual); + } +}