Skip to content

Commit

Permalink
Add support for jackson field ids (#868)
Browse files Browse the repository at this point in the history
* Add test demonstrating field id case

* Fix java 8 error

* Add missing import

* Add missing throws

* Cleanup unused imports

* Cleanup test

* Implement jackson field ids

* Address feedback from @komamitsu

* Address feedback from @komamitsu

* Address feedback from @komamitsu
  • Loading branch information
brenbar authored Feb 11, 2025
1 parent f480b7b commit 1cbd05f
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class MessagePackGenerator
private static final ThreadLocal<OutputStreamBufferOutput> 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;
Expand Down Expand Up @@ -188,28 +189,32 @@ 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(
int features,
ObjectCodec codec,
OutputStream out,
MessagePack.PackerConfig packerConfig,
boolean reuseResourceInGenerator)
boolean reuseResourceInGenerator,
boolean supportIntegerKeys)
throws IOException
{
super(features, codec);
this.output = out;
this.messagePacker = packerConfig.newPacker(getMessageBufferOutputForOutputStream(out, reuseResourceInGenerator));
this.packerConfig = packerConfig;
this.nodes = new ArrayList<>();
this.supportIntegerKeys = supportIntegerKeys;
}

private MessageBufferOutput getMessageBufferOutputForOutputStream(
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Object> valueDeser, TypeDeserializer valueTypeDeser, NullValueProvider nuller,
Set<String> ignorable, Set<String> includable)
{
super(src, keyDeser, valueDeser, valueTypeDeser, nuller, ignorable, includable);
}

@Override
protected MapDeserializer withResolved(KeyDeserializer keyDeser, TypeDeserializer valueTypeDeser,
JsonDeserializer<?> valueDeser, NullValueProvider nuller, Set<String> ignorable,
Set<String> includable)
{
return new MessagePackMapDeserializer(this, keyDeser, (JsonDeserializer<Object>) 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<Object, Object> map = new HashMap<>();
map.put(1, "one");
map.put("2", "two");

byte[] bytes = mapper.writeValueAsBytes(map);
Map<Object, Object> deserializedInit = mapper.readValue(bytes, new TypeReference<Map<Object, Object>>() {});

Map<Object, Object> expected = new HashMap<>(map);
Map<Object, Object> 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<Object, Object> map = new HashMap<>();
map.put(1, "one");
map.put("2", "two");

byte[] bytes = mapper.writeValueAsBytes(map);
Map<Object, Object> deserializedInit = mapper.readValue(bytes, new TypeReference<Map<Object, Object>>() {});

Map<Object, Object> expected = new HashMap<>();
expected.put("1", "one");
expected.put("2", "two");
Map<Object, Object> actual = new HashMap<>(deserializedInit);

assertEquals(expected, actual);
}
}

0 comments on commit 1cbd05f

Please sign in to comment.