LocalDateTimeシーケンス化からグローバル一貫性シーケンス化を検討する


日アーチの一卒は尽きず、功は唐に寄付しないで海に入った.
くさび
前の2週間にSpringSecurity1編の文章募集.を3編出しましたが、今週は簡単で使いやすい文章を書いて、頭を変えて、休みたいと思っています.
今日書くのはこの文章です: LocalDateTime .
このタイトルは人の話に似ていないように見えますが、とても公式な感じがします.まず、私たちのテーマは何ですか.LocalDateTimeのシーケンス化を説明することで、プロジェクト全体のすべてのシーケンス化処理を引き出し、一致させます.
私たちのプロジェクトには一般的に2つのシーケンス化があります.
1つはSpringMVCの公式のシーケンス化です.つまり、Springが作ってくれたシーケンス化です.例えば、インタフェースの上にResponseBodyの注釈を打って、SpringMVCのメッセージ変換器がシーケンス化してくれます.
もう一つは私たちのプロジェクト内のシーケンス化です.自分で定義したJsonUtilでも、あなたが導入したサードパーティJSON処理ツール(例えばFastJson)でも、私たちのプロジェクト内部のシーケンス化と言えるでしょう.
この2つが異なると、シーケンス化されたデータに結果が異なる場合があるので、それを防ぐために、今日はプロジェクトのシーケンス化について検討します.
1.例を挙げる
まず例を挙げて、シーケンス化が一致しなければどのような効果が現れるかを見てみましょう.
@GetMapping("/api/anon")
    public ApiResult test01() {
        return ApiResult.ok("      ");
    }

これは一般的なアクセスインタフェースで、次の結果が返されます.
{
    "code": 200,
    "msg": "    ",
    "data": {
        "    ": "      "
    },
    "timestamp": "2020-07-19T23:07:07.738",
    "fail": false,
    "success": true
}

ここではtimestampのシーケンス化結果に注意する必要があります.timestampはLocalDateTimeタイプで、SpringMVCのメッセージ変換器がLocalDateTimeをシーケンス化する際に特別な処理はなく、LocalDateTimeのtoString()メソッドを直接呼び出したので、このシーケンス化結果の中にTがあります.
しかし、ここでのシーケンス化が他のスキームを使用している場合、このシーケンス化の結果は異なる体験になるかもしれません.私のプロジェクトでもJacksonを採用してシーケンス化しています(Springでも使用されています).私たちが定義したJsonUtilがLocalDateTimeをシーケンス化するとどのような結果になるかを見ることができます.
@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に直接データを書くインタフェースもあります.この場合、JSONデータを書くのが一般的です.例えば、
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におけるシーケンス化とが一致する必要がある.
2.実操案
プロジェクトにおけるシーケンス化の一貫性を保つ必要性について述べた(必要だと思うハハ).
では、この一致性をすれば、次のように言えます.Springのシーケンス化で戻ってきたオブジェクトのLocalDateTimeタイプの変数をシーケンス化したい場合は、簡単です.
public class ApiResult implements Serializable {

    private static final Map 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注釈を付けるのは簡単ですが、これはグローバルではなく、どの変数にどの変数を加えるかが有効になります.
グローバルに有効にするには、Springの構成でSpringで使用されているObjectMapperを修正する必要があります.Jacksonを知っている仲間は知っているはずです.シーケンス化された様々な構成はこのObjectMapperに配置されています.知らなくても大丈夫です.あなたは今知っています.
では、SpringObjectMapperを構成することで、グローバルに有効にすることができます.
@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizer() {
        return builder -> {
            builder.locale(Locale.CHINA);
            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
}
timestampの間にそのTは存在しません.私たちはLocalDateTimeのシーケンス化案に参加したからです.
しかし、それだけではだめです.これはLocalDateTimeのグローバルシーケンス化にすぎません.私たちは自分のツールクラスもSpringと一致させる必要があります.
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
    {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();

        //       mapper      ,                   
        // 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 builderから出てきたObjectMapperについて、前のステップに進み、自分の望む属性を設定します.
コードの中の注釈のあれも1つのシーケンス化の変換をして、もしあなたのプロジェクトの中で比較的に長いLONGタイプの数字を使ったら、JSが完全な数字を手に入れられないかもしれません.javaの中のlongタイプはJSのnumberタイプより少し長いので、この時あなたはStringに変換してフロントに行かなければなりません.それは正しい数字を手に入れることができます.必要があれば、このセクションを開くことができます.
最後の一言は、builderから出てきたObjectMapperをツールクラスのObjectMapperに割り当てることです.そうすると、2人は実際に1つのアドレスを指しています.つまり、同じオブジェクトを使用してシーケンス化されています.結果はもちろん同じです.
後記
今日の LocalDateTime はここに着いて、みんなに役に立つことを望みます.
本文のコードは私も前のSpringSecruityのdemoの中に置いて、みんなは直接中に行ってクラス名を検索することができます.
本明細書のコード:コードクラウドアドレスGitHubアドレス
日アーチの一卒は尽きず、功は唐に寄付しないで海に入った.
あなた达のすべての点賛コレクションと評論はすべて私の知識に対して出力した莫大な肯定で、もし文の中に何か間違いや疑問点があれば、あるいは私の指導に対して評論区の下で伝言を残して、一緒に討論することができます.
私は耳で、ずっと知識の出力をしたい偽の文芸プログラマーです.