Files
int-layer/src.bak/main/java/com/example/util/FirestoreTimestampDeserializer.java
2026-02-20 08:42:45 +00:00

75 lines
3.8 KiB
Java

/*
* Copyright 2025 Google. This software is provided as-is, without warranty or representation for any use or purpose.
* Your use of it is subject to your agreement with Google.
*/
package com.example.util;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.google.cloud.Timestamp;
import java.io.IOException;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Custom Jackson Deserializer for com.google.cloud.Timestamp.
* Handles deserialization from embedded objects (direct Timestamp instances),
* ISO 8601 strings, and JSON objects with "seconds" and "nanos" fields.
*/
public class FirestoreTimestampDeserializer extends JsonDeserializer<Timestamp> {
private static final Logger logger = LoggerFactory.getLogger(FirestoreTimestampDeserializer.class);
@Override
public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonToken token = p.getCurrentToken();
if (token == JsonToken.VALUE_EMBEDDED_OBJECT) {
// This is the ideal case when ObjectMapper.convertValue gets a direct Timestamp object
Object embedded = p.getEmbeddedObject();
if (embedded instanceof Timestamp) {
logger.debug("FirestoreTimestampDeserializer: Deserializing from embedded Timestamp object: {}", embedded);
return (Timestamp) embedded;
}
} else if (token == JsonToken.VALUE_STRING) {
// Handles cases where the timestamp is represented as an ISO 8601 string
String timestampString = p.getText();
try {
logger.debug("FirestoreTimestampDeserializer: Deserializing from String: {}", timestampString);
return Timestamp.parseTimestamp(timestampString);
} catch (IllegalArgumentException e) {
logger.error("FirestoreTimestampDeserializer: Failed to parse timestamp string: '{}'", timestampString, e);
throw new IOException("Failed to parse timestamp string: " + timestampString, e);
}
} else if (token == JsonToken.START_OBJECT) {
// This is crucial for handling the "Cannot deserialize ... from Object value (token JsonToken.START_OBJECT)" error.
// It assumes the object represents { "seconds": X, "nanos": Y }
logger.debug("FirestoreTimestampDeserializer: Deserializing from JSON object.");
// Suppress the unchecked warning here, as we expect a Map<String, Number>
@SuppressWarnings("unchecked")
Map<String, Number> map = p.readValueAs(Map.class);
if (map != null && map.containsKey("seconds") && map.containsKey("nanos")) {
Number secondsNum = map.get("seconds");
Number nanosNum = map.get("nanos");
if (secondsNum != null && nanosNum != null) {
Long seconds = secondsNum.longValue();
Integer nanos = nanosNum.intValue();
return Timestamp.ofTimeSecondsAndNanos(seconds, nanos);
}
}
logger.error("FirestoreTimestampDeserializer: JSON object missing 'seconds' or 'nanos' fields, or fields are not numbers.");
}
// If none of the above formats match, log an error and delegate to default handling
logger.error("FirestoreTimestampDeserializer: Unexpected token type for Timestamp deserialization. Expected Embedded Object, String, or START_OBJECT. Got: {}", token);
// This will likely re-throw an error indicating inability to deserialize.
return (Timestamp) ctxt.handleUnexpectedToken(Timestamp.class, p);
}
}