Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

[PAN-2946] refactor normalizeKeys method #1826

Merged
merged 7 commits into from
Aug 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
package tech.pegasys.pantheon.config;

import static java.nio.charset.StandardCharsets.UTF_8;
import static tech.pegasys.pantheon.config.JsonUtil.normalizeKeys;

import java.io.IOException;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Streams;
import com.google.common.io.Resources;
Expand Down Expand Up @@ -133,25 +132,4 @@ private long parseLong(final String name, final String value) {
+ "'");
}
}

/* Converts all to lowercase for easier lookup since the keys in a 'genesis.json' file are assumed
* case insensitive.
*/
private static ObjectNode normalizeKeys(final ObjectNode genesis) {
final ObjectNode normalized = JsonUtil.createEmptyObjectNode();
genesis
.fields()
.forEachRemaining(
entry -> {
final String key = entry.getKey();
final JsonNode value = entry.getValue();
final String normalizedKey = key.toLowerCase(Locale.US);
if (value instanceof ObjectNode) {
normalized.set(normalizedKey, normalizeKeys((ObjectNode) value));
} else {
normalized.set(normalizedKey, value);
}
});
return normalized;
}
}
35 changes: 31 additions & 4 deletions config/src/main/java/tech/pegasys/pantheon/config/JsonUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package tech.pegasys.pantheon.config;

import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
Expand All @@ -28,6 +29,32 @@

public class JsonUtil {

/**
* Converts all the object keys (but none of the string values) to lowercase for easier lookup.
* This is useful in cases such as the 'genesis.json' file where all keys are assumed to be case
* insensitive.
*
* @param objectNode The ObjectNode to be normalized
* @return a copy of the json object with all keys in lower case.
*/
public static ObjectNode normalizeKeys(final ObjectNode objectNode) {
final ObjectNode normalized = JsonUtil.createEmptyObjectNode();
objectNode
.fields()
.forEachRemaining(
entry -> {
final String key = entry.getKey();
final JsonNode value = entry.getValue();
final String normalizedKey = key.toLowerCase(Locale.US);
if (value instanceof ObjectNode) {
normalized.set(normalizedKey, normalizeKeys((ObjectNode) value));
} else {
normalized.set(normalizedKey, value);
}
});
return normalized;
}

/**
* Get the string representation of the value at {@code key}. For example, a numeric value like 5
* will be returned as "5".
Expand Down Expand Up @@ -120,7 +147,7 @@ public static boolean getBoolean(
}

public static ObjectNode createEmptyObjectNode() {
ObjectMapper mapper = getObjectMapper();
final ObjectMapper mapper = getObjectMapper();
return mapper.createObjectNode();
}

Expand All @@ -140,7 +167,7 @@ public static ObjectNode objectNodeFromString(
final JsonNode jsonNode = objectMapper.readTree(jsonData);
validateType(jsonNode, JsonNodeType.OBJECT);
return (ObjectNode) jsonNode;
} catch (IOException e) {
} catch (final IOException e) {
// Reading directly from a string should not raise an IOException, just catch and rethrow
throw new RuntimeException(e);
}
Expand All @@ -152,7 +179,7 @@ public static String getJson(final Object objectNode) throws JsonProcessingExcep

public static String getJson(final Object objectNode, final boolean prettyPrint)
throws JsonProcessingException {
ObjectMapper mapper = getObjectMapper();
final ObjectMapper mapper = getObjectMapper();
if (prettyPrint) {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objectNode);
} else {
Expand Down Expand Up @@ -209,7 +236,7 @@ public static Optional<ArrayNode> getArrayNode(
}

private static Optional<JsonNode> getValue(final ObjectNode node, final String key) {
JsonNode jsonNode = node.get(key);
final JsonNode jsonNode = node.get(key);
if (jsonNode == null || jsonNode.isNull()) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
Expand All @@ -29,7 +31,40 @@
import org.junit.Test;

public class JsonUtilTest {
private ObjectMapper mapper = new ObjectMapper();
private final ObjectMapper mapper = new ObjectMapper();

@Test
public void normalizeKeys_cases() {
final List<String> cases =
List.of(
"lower",
"CAPS",
"Proper",
"camelCase",
"snake_case",
"SHOUT_CASE",
"dash-case",
"!@#$%^&*(){}[]\\|-=_+;':\",.<>/?`~");
final ObjectNode base = mapper.createObjectNode();
for (final String s : cases) {
base.put(s, s);
}

final ObjectNode normalized = JsonUtil.normalizeKeys(base);
for (final String s : cases) {
assertThat(base.get(s).asText()).isEqualTo(normalized.get(s.toLowerCase(Locale.US)).asText());
}
}

@Test
public void normalizeKeys_depth() {
final ObjectNode base = mapper.createObjectNode();
base.putObject("DEEP").putObject("deeper").put("deeperER", "DEEPEST");

final ObjectNode normalized = JsonUtil.normalizeKeys(base);
assertThat(base.get("DEEP").get("deeper").get("deeperER").asText())
.isEqualTo(normalized.get("deep").get("deeper").get("deeperer").asText());
}

@Test
public void getLong_nonExistentKey() {
Expand Down Expand Up @@ -413,7 +448,7 @@ public void objectNodeFromMap() {
subMap.put("d", 2L);
map.put("subtree", subMap);

ObjectNode node = JsonUtil.objectNodeFromMap(map);
final ObjectNode node = JsonUtil.objectNodeFromMap(map);
assertThat(node.get("a").asInt()).isEqualTo(1);
assertThat(node.get("b").asInt()).isEqualTo(2);
assertThat(node.get("subtree").get("c").asText()).isEqualTo("bla");
Expand Down