その日のアーチとポーンには終わりがなく、仕事は無駄になることなく、海に流れ着きます。
ウェッジ
この23つの記事と投稿したので、今週は気分転換と息抜きに、シンプルで役に立つ簡単なことを書こうと思います。
LocalDateTimeシリアライゼーションからグローバル一貫シリアライゼーションを体験する。
今日はこのことについて書きます。
プロジェクトには一般的に2種類のシリアライゼーションが存在します。
例えば、インターフェイスの上にResponseBodyアノテーションを置くと、SpringMVCのメッセージコンバータがシリアライズの手助けをしてくれます。
もう1つは、プロジェクト内でのシリアライズです。独自のJsonUtilを定義する場合でも、サードパーティのJSON処理ツールを導入する場合でも、プロジェクト内でシリアライズを行うことができます。
この2つが同じでない場合、シリアル化されたデータが同じ結果に見えないことがあります。このような状況を防ぐために、今日はプロジェクトでのシリアル化について調べます。
まず、シリアライゼーションに一貫性がないとどうなるかを例で見てみましょう。
@GetMapping("/api/anon")
public ApiResult test01() {
return ApiResult.ok("匿名アクセス成功");
}
これはインターフェースへのごく一般的なアクセスで、以下のようなものを返します:
{
"code": 200,
"msg": "リクエスト成功",
"data": {
"リクエスト成功": "匿名アクセス成功"
},
"timestamp": "2020-07-19T23:07:07.738",
"fail": false,
"success": true
}
タイムスタンプはLocalDateTime型ですが、SpringMVCのメッセージコンバータはLocalDateTimeのシリアライズを特別扱いしておらず、LocalDateTimeの**toString()**メソッドを直接呼び出します。)**メソッドを直接呼び出すので、このシリアライズ結果の真ん中にTがあります。
@Slf4j
public class JacksonUtil {
public static ObjectMapper objectMapper = new ObjectMapper();
/**
* JavaオブジェクトをJSON文字列に変換する
*
* @param object
* @return
*/
public static String toJsonString(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
log.error("The JacksonUtil toJsonString is error :
", e);
throw new RuntimeException();
}
}
}
シリアライズツールクラスはこのようになっており、上記のようにApiResultをシリアライズして結果を確認します:
{
"code": 400,
"msg": "リクエスト失敗",
"timestamp": {
"month": "JULY",
"year": 2020,
"dayOfMonth": 19,
"hour": 23,
"minute": 25,
"monthValue": 7,
"nano": 596000000,
"second": 2,
"dayOfYear": 201,
"dayOfWeek": "SUNDAY",
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"fail": true,
"success": false
}
JacksonのデフォルトのObjectMapperは、結果のシリアライズの下で、それがシリアライズされ、最終的に文字列に変換されるため、この幽霊であり、あなたがそれを取得した場合、データのフロントエンドは確かに時間型の通常の変換ではありません。
LocalDateTimeは小宇宙に過ぎず、文字列であっても、シリアライゼーションの構成が異なれば、影響も異なります。
HTTPインターフェイスのサードパーティ製のインターフェイスの実際のプロジェクトでは、一般的にデータを転送するプロジェクトのJSONツールクラスの後に文字列にシリアライズされ、その後、過去に転送される必要があります、シリアライズのスキームが異なる場合は、過去に転送されたデータのシリアライズの過程でされる可能性があります希望されていません。
HttpServeletResponse
に直接データを書き込むインターフェースもあります:
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.setHeader("Cache-Control", "no-cache");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().println(JacksonUtil.toJsonString(ApiResult.fail(authException.getMessage())));
response.getWriter().flush();
}
ここでは、このApiResultを直接シリアライズするためにツールクラスを使用しています。フロントエンドに渡されたデータは、上記の例でも表示されますが、LocalDateTimeのシリアライズ結果は望ましいものではありません。
そのため、プロジェクトのシリアライズとSpringのシリアライズの整合性を保つ必要があります。
実践プログラム
プロジェクトにおけるシリアライゼーションの一貫性を保つ必要性は前述の通り。
では、この一貫性を目指すなら、こう言えるでしょう。
SpringのシリアライゼーションでLocalDateTime型の変数を返したオブジェクトをシリアライズするのは簡単です:
public class ApiResult implements Serializable {
private static final Map<String, String> map = new HashMap<>(1);
private int code;
private String msg;
private Object data;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
この変数の上にJsonFormatアノテーションを追加するだけでOKですが、これはグローバルではなく、どの変数を追加しても有効になります。
グローバルな効果を行うには、SpringのObjectMapperの使用を変更するには、Springの構成に行く必要があり、ジャクソンパートナーは、様々な構成のシリアライゼーションは、このObjectMapperで構成されていることを知っている必要があります理解し、それが大丈夫であることを知っていない、あなたは今知っています。
そして、SpringのObjectMapperのコンフィギュレーションでグローバルに実行できます:
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder.locale(Locale.Japan);
builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
builder.modules(javaTimeModule);
};
}
}
Jackson2ObjectMapperBuilderCustomizer
これは、上記のコードで行っているように、何らかの直列化スキームを , に追加することで実現できます:
{
"code": 200,
"msg": "リクエスト成功",
"data": {
"リクエスト成功": "匿名アクセス成功"
},
"timestamp": "2020-07-20 00:06:12",
"fail": false,
"success": true
}
LocalDateTimeシリアライゼーションスキームが追加されたため、timestampの真ん中のTはもう存在しません。
しかし、それだけではありません。これはLocalDateTimeをグローバルにシリアライズしているだけなので、独自のツールもSpringのものと一致させる必要があります:
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
{
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// マッパーオブジェクトにこのメソッドを設定することで、シリアライズされたオブジェクトはすべて、変更されたルールに従ってシリアライズされる。
// Include.Include.ALWAYS
// Include.NON_DEFAULT 属性はデフォルトではシリアライズされない
// Include.NON_EMPTY 属性が空であるか、NULLがシリアライズされていない場合、返されるjsonはフィールドの
// Include.NON_NULL 属性がNULLで直列化されない
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 特殊文字とエスケープ文字を許可する
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
// シングルクォート可
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
/**
* Long、BigIntegerをStringにシリアライズする。
*/
// SimpleModule simpleModule = new SimpleModule();
//
// simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
// simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
// simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
//
// objectMapper.registerModule(simpleModule);
// ツールクラスのobjectMapperをSpringのobjectMapperに置き換える。
JacksonUtil.objectMapper = objectMapper;
return objectMapper;
}
Jackson2ObjectMapperBuilder
このコードは前のステップに続き、ビルダーから出てきたObjectMapperで何かを行い、必要なプロパティのセットを設定します。
あなたのプロジェクトは、長いLONG型の番号を使用する場合、JSにつながる可能性があり、完全な番号を取得することはできません、JavaのLONG型からJSの数値型に少し長いので、この時間は、フォアグラウンドに文字列に変換する必要があります、それは正しい番号を取得することができるようになります、あなたは、必要性がある場合することができます直列変換を行うことです。この段落を開きます。
最後の文章はより重要で、ツールクラスのObjectMapperに割り当てられたObjectMapperのビルダーは、この場合、実際にはアドレスを指しています。
あとがき
LocalDateTimeシリアライゼーションからのグローバル一貫シリアライゼーション
今日のところは以上です。
この記事では、私もSpringSecruityのデモにコードを入れて、あなたは直接検索クラス名の内部に行くことができます見つけることができます。
この記事のコード:
その日のアーチとポーンには終わりがなく、仕事は無駄になることなく、海に流れ着きます。
ナレッジ・エクスポーターに憧れていた似非文系プログラマーのイアです、それではまた次回。