Jackson ObjectMapper-指定对象属性的序列化顺序

我正在实现一个 RESTful Web 服务,其中用户必须在发送请求的同时发送一个签名的验证令牌,这样我就可以确保请求没有被中间人篡改。我当前的实现如下。

Verification token is a VerifData object serialized into a String and then hashed and encrypted.

class VerifData {
int prop1;
int prop2;
}

在我的服务中,我将要序列化的数据放入 VerifData 的实例中,然后使用 Jackson ObjectMapper 对其进行序列化,并将这些数据连同验证令牌一起传递给验证引擎。

VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);

但似乎每次启动应用程序容器时,ObjectMapper 映射到字符串的属性顺序都会发生变化。

只有一次

{"prop1":12345,"prop2":67890}

and another time it would be

{"prop2":67890,"prop1":12345}

因此,如果客户端已经将 VerifData 实例序列化为第一个 String,那么即使它是正确的,也有50% 的可能性会失败。

有办法解决吗?我是否可以指定 ObjectMapper 映射属性的顺序(如升序) ?或者有没有其他方法可以最好地实现这个验证步骤。客户端和服务器实现都是由我开发的。我使用 JavaSecurityAPI 进行签名和验证。

81864 次浏览

来自 Jackson 注释文档:

// ensure that "id" and "name" are output before other properties
@JsonPropertyOrder({ "id", "name" })


// order any properties that don't have explicit setting using alphabetic order
@JsonPropertyOrder(alphabetic=true)

这些注释很有用,但是在任何地方应用都很麻烦。您可以将整个 ObjectMapper配置为以这种方式工作

现在的杰克逊版本:

objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

老版本:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

From Duncan McGregor's answer: 最好这样使用:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

因为 MapperFeature 是用于 XML 的,并且带有 jackson-database,这是不需要的..。

In Spring Boot you can add this behaviour globally by adding the following to your Application entry point class:

  @Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {


Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);


return builder;
}

There is an easier way in Spring Boot by specifying a property (in application.properties for example:

spring.jackson.mapper.sort_properties_alphabetically=true

在 Jackson 2.x 中,你可能正在使用它,使用:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

如果你在意外表,你也可以考虑 SerializationFeature.INDENT_OUTPUT

Note that you must serialize 地图 or Objects for this to sort correctly. If you serialize a JsonNode for example (from readTree), that won't be properly indented.

例子

import com.fasterxml.jackson.databind.*;


ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);


String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));

结果:

{
"hello" : {
"cruel" : "world"
}
}

不使用标志参数:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

The following 2 ObjectMapper configuration are required:

ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
或者
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY) < br >

定义用于 POJO 字段的属性序列化顺序
Note: does 没有 apply to java.util.Map serialization!

还有

ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
或者
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS) < br >

特性,确定在序列化之前是否首先对 java.util.Map条目进行排序


Spring Boot 配置示例(yaml) :

spring:
jackson:
mapper:
SORT_PROPERTIES_ALPHABETICALLY: true
serialization:
ORDER_MAP_ENTRIES_BY_KEYS: true

You can use mix-in and specify the order of properties as you like:

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;


import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;


@Component
public final class ObjectMapperUtils {


private static final ObjectMapper MAPPER = new ObjectMapper();


static {
MAPPER.addMixIn(Object.class, IdFirst.class);
}


@Bean
public ObjectMapper objectMapper() {
return MAPPER;
}


@JsonPropertyOrder({"id", "...", "..."})
private abstract static class IdFirst {}


}

我意识到这是一个老线程,但因为我正在寻找或一个答案,并在这里着陆,一些额外的信息可能是方便其他人。
我当前使用的@JsonProperty 注释(jackson-annotions-2.11.2)除了“ value”参数之外,还接受一个“ index”(数值)参数,该参数指定序列化过程中字段的顺序。

我今天发现了另一种方法,以防字母顺序不是你想要的排序顺序。在字段上添加@JsonProperty 注释会在写入其他字段时将其放在最后,如果其他字段没有注释的话。当我想指定一个不符合 Java 命名约定的属性名时,我发现了这个问题。

通过添加 index 属性,您可以定义 order。

@JsonProperty(index=20)
String prop1;


@JsonProperty(index=10)
String prop2;

将呈现:

{"prop2": "valueProp2", "prop1": "valueProp1"}

正如@Gary Rowe 提到的,我们可以使用 Jackson2ObjectMapperBuilder 对属性进行全局排序。 但是,要使其工作,您必须在类路径中使用 Jackson2ObjectMapperBuilder,它不是 Jackson 库的一部分。

根据 这份文件,spring-web 依赖项有 Jackson2ObjectMapperBuilder 文件,应该在类路径中。

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {


Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);


return builder;
}

您可以参考 这个了解其他可能的解决方案