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

Commit

Permalink
[PAN-2946] refactor normalizeKeys method (#1826)
Browse files Browse the repository at this point in the history
Move the `normalizeKeys` method into JsonUtil as Retesteth will re-use it.
  • Loading branch information
Danno Ferrin authored Aug 7, 2019
1 parent 21f8138 commit 6ef349c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 29 deletions.
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

0 comments on commit 6ef349c

Please sign in to comment.