From 47afafe47cc6ee6a53f3ae3c0539628621fa1d52 Mon Sep 17 00:00:00 2001 From: Libo Song Date: Wed, 25 Oct 2023 12:22:17 -0400 Subject: [PATCH] Map toString with keys in order. --- .../collections/SizeLimitingPyMap.java | 28 +++++++++++++++++ .../serialization/PyishObjectMapper.java | 8 +++++ .../jinjava/tree/ExpressionNodeTest.java | 30 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/src/main/java/com/hubspot/jinjava/objects/collections/SizeLimitingPyMap.java b/src/main/java/com/hubspot/jinjava/objects/collections/SizeLimitingPyMap.java index f688ac568..7fdab68c8 100644 --- a/src/main/java/com/hubspot/jinjava/objects/collections/SizeLimitingPyMap.java +++ b/src/main/java/com/hubspot/jinjava/objects/collections/SizeLimitingPyMap.java @@ -7,7 +7,10 @@ import com.hubspot.jinjava.interpret.TemplateError.ErrorType; import com.hubspot.jinjava.objects.PyWrapper; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; public class SizeLimitingPyMap extends PyMap implements PyWrapper { private int maxSize; @@ -39,6 +42,31 @@ public Object put(String s, Object o) { return super.put(s, o); } + @Override + public String toString() { + if (isEmpty()) { + return "{}"; + } + List> list = entrySet() + .stream() + .sorted(Entry.comparingByKey()) + .collect(Collectors.toList()); + Iterator> i = list.iterator(); + if (!i.hasNext()) return "{}"; + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (;;) { + Entry e = i.next(); + String key = e.getKey(); + Object value = e.getValue(); + sb.append(key); + sb.append('='); + sb.append(value == this ? "(this Map)" : value); + if (!i.hasNext()) return sb.append('}').toString(); + sb.append(',').append(' '); + } + } + @Override public void putAll(Map m) { if (m == null) { diff --git a/src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java b/src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java index bf347b0e4..4c87cf05a 100644 --- a/src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java +++ b/src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java @@ -4,9 +4,11 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.google.common.annotations.Beta; @@ -49,6 +51,12 @@ private static ObjectMapper getPyishObjectMapper() { .addSerializer(PyishSerializable.class, PyishSerializer.INSTANCE) ); mapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer()); + mapper.setConfig( + mapper + .getSerializationConfig() + .with(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) + .with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) + ); return mapper; } diff --git a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java index ca613ef99..0f27abd2e 100644 --- a/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java +++ b/src/test/java/com/hubspot/jinjava/tree/ExpressionNodeTest.java @@ -11,6 +11,8 @@ import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.UnknownTokenException; import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; import org.junit.Test; public class ExpressionNodeTest extends BaseInterpretingTest { @@ -210,6 +212,34 @@ public void itEscapesValueWhenContextSet() throws Exception { assertThat(val("{{ a }}")).isEqualTo("foo < bar"); } + @Test + public void itTestsMapSerializedInOrder() { + Jinjava jinjava = new Jinjava(); + JinjavaInterpreter interpreter = jinjava.newInterpreter(); + context = interpreter.getContext(); + Map fooMap = new HashMap<>(); + fooMap.put("bbc", "b"); + fooMap.put("abc", "a"); + context.put("a", fooMap); + + String output = new TreeParser(interpreter, "{{a}}") + .buildTree() + .getChildren() + .getFirst() + .render(interpreter) + .getValue(); + assertThat(output).isEqualTo("{abc=a, bbc=b}"); + } + + @Test + public void itTestsMapSerializedInOrderWithLegacy() { + Map fooMap = new HashMap<>(); + fooMap.put("bbc", "b"); + fooMap.put("abc", "a"); + context.put("a", fooMap); + assertThat(val("{{ a }}")).isEqualTo("{'abc': 'a', 'bbc': 'b'}"); + } + private String val(String jinja) { return parse(jinja).render(interpreter).getValue(); }