Skip to content

Commit

Permalink
[pinpoint-apm#11021] Add JsonFields for dynamic json field names
Browse files Browse the repository at this point in the history
  • Loading branch information
emeroad committed May 16, 2024
1 parent 4a2b4cc commit 5bd758f
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.navercorp.pinpoint.common.server.util.json;

public interface JsonField<K, V> {
K name();

V value();

static <K, V> JsonField<K, V> of(K name, V value) {
return new JsonObjectField<>(name, value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.navercorp.pinpoint.common.server.util.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.collect.Iterators;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

@JsonSerialize(using = JsonFields.Serializer.class)
public class JsonFields<K, V> implements Iterable<JsonField<String, V>>{

private final Map<K, V> node;
private final Function<K, String> nameMapper;

JsonFields(Map<K, V> node, Function<K, String> nameMapper) {
this.node = Objects.requireNonNull(node, "node");
this.nameMapper = Objects.requireNonNull(nameMapper, "nameMapper");
}

@Override
public String toString() {
return node.toString();
}

@Override
public Iterator<JsonField<String, V>> iterator() {
return Iterators.transform(node.entrySet().iterator(), this::toJsonField);
}

private JsonField<String, V> toJsonField(Map.Entry<K, V> entry) {
K key = entry.getKey();
String name = this.nameMapper.apply(key);
return new JsonStringField<>(name, entry.getValue());
}

public static class Serializer<K, V> extends JsonSerializer<JsonFields<K, V>> {
@Override
public void serialize(JsonFields<K, V> fields, JsonGenerator gen, SerializerProvider serializers) throws IOException {
final Function<K, String> nameMapper = fields.nameMapper;

gen.writeStartObject();
for (Map.Entry<K, V> entry : fields.node.entrySet()) {
String name = nameMapper.apply(entry.getKey());
gen.writeFieldName(name);
gen.writeObject(entry.getValue());
}
gen.writeEndObject();
}
}

public static <K, V> Builder<K, V> newBuilder() {
return new Builder<>(Object::toString);
}

public static <K, V> Builder<K, V> newBuilder(Function<K, String> keyMapper) {
return new Builder<>(keyMapper);
}

public static class Builder<K, V> {
private final Map<K, V> node;
private final Function<K, String> nameMapper;

Builder(Function<K, String> nameMapper) {
this.node = new LinkedHashMap<>();
this.nameMapper = Objects.requireNonNull(nameMapper, "nameMapper");
}

public Builder<K, V> addField(K name, V value) {
Objects.requireNonNull(name, "name");
node.put(name, value);
return this;
}

public Builder<K, V> addField(JsonField<K, V> field) {
Objects.requireNonNull(field, "field");
node.put(field.name(), field.value());
return this;
}

public JsonFields<K, V> build() {
return new JsonFields<>(node, nameMapper);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.navercorp.pinpoint.common.server.util.json;

import java.util.Objects;

public class JsonObjectField<K, V> implements JsonField<K, V> {
private final K name;
private final V value;

JsonObjectField(K name, V value) {
this.name = Objects.requireNonNull(name, "name");
this.value = value;
}

public K name() {
return name;
}

public V value() {
return value;
}

@Override
public String toString() {
return name.toString() + ':' + value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.navercorp.pinpoint.common.server.util.json;

import java.util.Objects;

public class JsonStringField<V> implements JsonField<String, V> {
private final String name;
private final V value;

JsonStringField(String name, V value) {
this.name = Objects.requireNonNull(name, "name");
this.value = value;
}

public String name() {
return name;
}

public V value() {
return value;
}

@Override
public String toString() {
return name + ':' + value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.navercorp.pinpoint.common.server.util.json;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.LinkedHashMap;
import java.util.Map;

class JsonFieldsTest {

private final Logger logger = LogManager.getLogger(this.getClass());

ObjectMapper mapper = Jackson.newMapper();

@Test
void testObjectKey() throws Exception {
Map<String, String> map = new LinkedHashMap<>();
map.put("key1-1", "value1");
map.put("key2-2", "value2");
String mapString = mapper.writeValueAsString(map);

JsonFields.Builder<Key, String> builder = JsonFields.newBuilder(Key::name);
builder.addField(new Key("key1", 1), "value1");
builder.addField(new Key("key2", 2), "value2");
JsonFields<Key, String> jsonObject = builder.build();

String jsonFieldStr = mapper.writeValueAsString(jsonObject);

Assertions.assertEquals(mapString, jsonFieldStr);
}

public record Key(String key, int value) {
public String name() {
return key + '-' + value;
}
}

@Test
void testStringKey() throws Exception {

Map<String, String> map = new LinkedHashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
String mapString = mapper.writeValueAsString(map);

JsonFields.Builder<String, String> builder = JsonFields.newBuilder();
for (Map.Entry<String, String> entry : map.entrySet()) {
builder.addField(entry.getKey(), entry.getValue());
}

JsonFields<String, String> jsonObject = builder.build();
String jsonFieldStr = mapper.writeValueAsString(jsonObject);

Assertions.assertEquals(mapString, jsonFieldStr);
}


@Test
void test_addField() throws Exception {
Map<String, String> map = new LinkedHashMap<>();
map.put("key1", "value1");
String mapString = mapper.writeValueAsString(map);

JsonFields.Builder<String, String> builder = JsonFields.newBuilder();
JsonFields<String, String> value = builder.addField(JsonField.of("key1", "value1"))
.build();

String jsonFieldStr = mapper.writeValueAsString(value);

Assertions.assertEquals(mapString, jsonFieldStr);
}

@Test
void testIterator() {
JsonFields.Builder<String, String> builder = JsonFields.newBuilder();
JsonFields<String, String> fields = builder.addField("k", "v")
.build();

JsonField<String, String> v1 = fields.iterator().next();
Assertions.assertEquals("k", v1.name());
Assertions.assertEquals("v", v1.value());
}

@Test
public void jsonNodeFactory_sample() throws Exception {
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode jsonNodes = factory.objectNode();
jsonNodes.set("string", factory.pojoNode("value"));
jsonNodes.put("int", 1);

String json = mapper.writeValueAsString(jsonNodes);
logger.debug("json {}", json);
}
}

0 comments on commit 5bd758f

Please sign in to comment.