如何在从 Jackson 中的字符串构造 JsonNode 时更改它的值
How to alter the value of a JsonNode while constructing it from a string in Jackson
我有一个 JSON 字符串,我想在使用 Jackson 库构建 JsonNode 时更改该值。
例如:-
input: {"name":"xyz","price":"90.00"}
output:{"name":"xyz-3","price":90.90}
我创建了自己的 JsonFactory 并传递了自己的 Parser。但我只能更改键,不能更改与键关联的值。
代码:
private static ObjectMapper create() {
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory() {
@Override
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt));
}
@Override
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(in, ctxt));
}
@Override
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(r, ctxt));
}
@Override
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt, recyclable));
}
});
private static final class MyParser extends JsonParserDelegate {
private MyParser(JsonParser d) {
super(d);
}
@Override
public String getCurrentName() throws IOException, JsonParseException {
....
}
@Override
public String getText() throws IOException, JsonParseException {
...
}
@Override
public Object getCurrentValue() {
...
}
@Override
public String getValueAsString() throws IOException {
...
}
@Override
public String getValueAsString(String defaultValue) throws IOException {
...
}
}
下面是从字符串构造 JsonNode 的代码。
mapper.readTree(jsonStr);
在这种情况下,当调用 readTree 方法时,不会调用 getCurrentValue
或 getValueAsString
方法,因此我无法在创建 JsonNode 本身时更改值。
json 字符串也可以不同。基本上我想从字符串构造一个 JsonNode 。所以绑定到特定的 schema/bean 在这里不是一个好的选择。
如何解决这个问题? TIA
为版本 2.7.4 添加更新代码:-
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
@Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
@Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
@Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
@Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
@Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
@Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
pom.xml:-
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.8.7</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.8.7</version>
</dependency>
您真的确定 Separation of Concerns 在已解析的数据中混合解析和更改是个好主意吗?
如果您仍然想这样做,您可以使用 Custom Deserializer 并按照您想要的方式处理您想要的字段名称和类型,例如:
class CustomDeserializer extends StdDeserializer<Entity> {
public CustomDeserializer(Class<Entity> t) {
super(t);
}
@Override
public Entity deserialize(JsonParser jp, DeserializationContext dc) throws IOException {
String name = null;
float price = 0;
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
switch (currentToken) {
case VALUE_STRING:
switch (jp.getCurrentName()) {
case "name":
name = jp.getText() + "-3"; // change this text to whatever you want;
break;
case "price":
price = Float.parseFloat(jp.getText()); // parse
break;
default:
break;
}
break;
default:
break;
}
}
return new Entity(name, price);
}
}
在注册您的自定义反序列化器后,它可以在您想要的任何对象映射器上运行:
@Test
public void customDeserialization() throws IOException {
// given
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Entity.class, new CustomDeserializer(Entity.class));
mapper.registerModule(module);
// when
Entity entity = mapper.readValue("{\"name\":\"xyz\",\"price\":\"90.00\"}", Entity.class);
// then
assertThat(entity.getName()).isEqualTo("xyz-3");
assertThat(entity.getPrice()).isEqualTo(90f);
}
编辑:2.7.*
和 2.9.*
之间存在细微差别。
虽然 2.9.*
能够通过
区分 double
和 float
getDoubleValue()
getFloatValue()
而不是 2.7.*
仅使用
getDoubleValue()
甚至 ID_NUMBER_FLOAT
个代币。
因此,您需要决定是否要保持向后兼容性。
您也可以覆盖两者,就像我在这里所做的那样。
这就是您定制所需的全部内容 MyParser
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
@Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
@Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
@Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
@Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
@Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
@Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
输出:{"name":"xyz-3","price":90.09}
您的代码看起来不错,并且已经过测试并且可以正常工作 ;)
我有一个 JSON 字符串,我想在使用 Jackson 库构建 JsonNode 时更改该值。 例如:-
input: {"name":"xyz","price":"90.00"}
output:{"name":"xyz-3","price":90.90}
我创建了自己的 JsonFactory 并传递了自己的 Parser。但我只能更改键,不能更改与键关联的值。
代码:
private static ObjectMapper create() {
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory() {
@Override
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt));
}
@Override
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(in, ctxt));
}
@Override
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(r, ctxt));
}
@Override
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt, recyclable));
}
});
private static final class MyParser extends JsonParserDelegate {
private MyParser(JsonParser d) {
super(d);
}
@Override
public String getCurrentName() throws IOException, JsonParseException {
....
}
@Override
public String getText() throws IOException, JsonParseException {
...
}
@Override
public Object getCurrentValue() {
...
}
@Override
public String getValueAsString() throws IOException {
...
}
@Override
public String getValueAsString(String defaultValue) throws IOException {
...
}
}
下面是从字符串构造 JsonNode 的代码。
mapper.readTree(jsonStr);
在这种情况下,当调用 readTree 方法时,不会调用 getCurrentValue
或 getValueAsString
方法,因此我无法在创建 JsonNode 本身时更改值。
json 字符串也可以不同。基本上我想从字符串构造一个 JsonNode 。所以绑定到特定的 schema/bean 在这里不是一个好的选择。
如何解决这个问题? TIA
为版本 2.7.4 添加更新代码:-
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
@Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
@Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
@Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
@Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
@Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
@Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
pom.xml:-
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.8.7</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.8.7</version>
</dependency>
您真的确定 Separation of Concerns 在已解析的数据中混合解析和更改是个好主意吗?
如果您仍然想这样做,您可以使用 Custom Deserializer 并按照您想要的方式处理您想要的字段名称和类型,例如:
class CustomDeserializer extends StdDeserializer<Entity> {
public CustomDeserializer(Class<Entity> t) {
super(t);
}
@Override
public Entity deserialize(JsonParser jp, DeserializationContext dc) throws IOException {
String name = null;
float price = 0;
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
switch (currentToken) {
case VALUE_STRING:
switch (jp.getCurrentName()) {
case "name":
name = jp.getText() + "-3"; // change this text to whatever you want;
break;
case "price":
price = Float.parseFloat(jp.getText()); // parse
break;
default:
break;
}
break;
default:
break;
}
}
return new Entity(name, price);
}
}
在注册您的自定义反序列化器后,它可以在您想要的任何对象映射器上运行:
@Test
public void customDeserialization() throws IOException {
// given
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Entity.class, new CustomDeserializer(Entity.class));
mapper.registerModule(module);
// when
Entity entity = mapper.readValue("{\"name\":\"xyz\",\"price\":\"90.00\"}", Entity.class);
// then
assertThat(entity.getName()).isEqualTo("xyz-3");
assertThat(entity.getPrice()).isEqualTo(90f);
}
编辑:2.7.*
和 2.9.*
之间存在细微差别。
虽然 2.9.*
能够通过
double
和 float
getDoubleValue()
getFloatValue()
而不是 2.7.*
仅使用
getDoubleValue()
甚至 ID_NUMBER_FLOAT
个代币。
因此,您需要决定是否要保持向后兼容性。
您也可以覆盖两者,就像我在这里所做的那样。
这就是您定制所需的全部内容 MyParser
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
@Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
@Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
@Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
@Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
@Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
@Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
输出:{"name":"xyz-3","price":90.09}
您的代码看起来不错,并且已经过测试并且可以正常工作 ;)