dianping/cat · error · JsonParseException

The date should be a string value

Error message

The date should be a string value

What it means

A Gson JsonParseException thrown by the custom TimestampTypeAdapter registered inside JsonBuilder when deserializing a JSON field into java.sql.Timestamp and the JSON element is not a JsonPrimitive — i.e. the value is an object, array, or null literal instead of a string. The adapter expects dates serialized as "yyyy-MM-dd HH:mm:ss" strings; only the 'not a primitive' shape produces this exact message (an unparseable string yields a wrapped ParseException instead).

Source

Thrown at cat-core/src/main/java/com/dianping/cat/helper/JsonBuilder.java:76

	private Gson m_gson = new GsonBuilder().registerTypeAdapter(Timestamp.class, new TimestampTypeAdapter())
							.setDateFormat("yyyy-MM-dd HH:mm:ss").setFieldNamingStrategy(m_fieldNamingStrategy).create();

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object parse(String json, Class clz) {
		return m_gson.fromJson(json, clz);
	}

	public String toJson(Object o) {
		return m_gson.toJson(o);
	}

	public class TimestampTypeAdapter implements JsonSerializer<Timestamp>, JsonDeserializer<Timestamp> {
		private final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
								throws JsonParseException {
			if (!(json instanceof JsonPrimitive)) {
				throw new JsonParseException("The date should be a string value");
			}

			try {
				Date date = format.parse(json.getAsString());
				return new Timestamp(date.getTime());
			} catch (ParseException e) {
				throw new JsonParseException(e);
			}
		}

		public JsonElement serialize(Timestamp src, Type arg1, JsonSerializationContext arg2) {
			String dateFormatAsString = format.format(new Date(src.getTime()));
			return new JsonPrimitive(dateFormatAsString);
		}
	}

}

View on GitHub (pinned to e815e74d4c)

Solutions

  1. Inspect the raw JSON for the Timestamp field; confirm it is a quoted "yyyy-MM-dd HH:mm:ss" string.
  2. Fix the producer to serialize timestamps as strings in that exact format.
  3. If nulls are legitimate, make the field nullable on the Java side so Gson skips the adapter, or preprocess the JSON replacing null dates with a sentinel string.
  4. Write your own TypeAdapter that returns null for non-primitive input instead of throwing, and register it on your Gson instance.

Example fix

// before
{"createTime": {"$date":"2026-01-01T00:00:00Z"}} // object -> JsonParseException

// after
{"createTime": "2026-01-01 00:00:00"}
Defensive patterns

Strategy: try-catch

Type guard

JsonElement el = jsonObject.get("createTime");
if (el == null || !el.isJsonPrimitive()) { /* treat as null/missing */ }

Try / catch

try { obj = jsonBuilder.fromJson(json, type); }
catch (JsonParseException e) { if (e.getMessage().contains("date should be a string")) { /* normalize payload or null the field */ } else throw e; }

Prevention

When it happens

Trigger: JsonBuilder.fromJson(json, type) (or any Gson parse using this adapter) where a Timestamp-typed field in the payload is {} / [] / null rather than a quoted date string. For example {"createTime": null} or {"createTime": {"$date": ...}} (Mongo-style extended JSON).

Common situations: Consuming JSON from systems that serialize dates as structured objects (BSON extended JSON, Jackson default array [y,m,d,...]) or that emit explicit nulls; changing a field type to Timestamp while old payloads still carry a different shape.

Related errors


AI-assisted analysis of dianping/cat@e815e74d4c (2026-08-14). Data as JSON: /api/errors/12da3dfa8be0db7a. Report an issue: GitHub.