SSE_ENVELOPE_FIELDS = new HashSet<>(Arrays.asList("event", "data", "id", "retry"));
+
+ private SseEventParser() {
+ // Utility class
+ }
+
+ /**
+ * Parse an SSE event using event-level discrimination.
+ *
+ * Constructs the full SSE envelope object with event, data, id, and retry fields,
+ * then deserializes it to the target union type.
+ *
+ * @param eventType The SSE event type (from event: field)
+ * @param data The SSE data content (from data: field)
+ * @param id The SSE event ID (from id: field), may be null
+ * @param retry The SSE retry value (from retry: field), may be null
+ * @param unionClass The target union class
+ * @param discriminatorProperty The property name used for discrimination (e.g., "event")
+ * @param The target type
+ * @return The deserialized object
+ */
+ public static T parseEventLevelUnion(
+ String eventType,
+ String data,
+ String id,
+ Long retry,
+ Class unionClass,
+ String discriminatorProperty) {
+ try {
+ // Determine if data should be parsed as JSON based on the variant's expected type
+ Object parsedData = parseDataForVariant(eventType, data, unionClass, discriminatorProperty);
+
+ // Construct the SSE envelope object
+ Map envelope = new HashMap<>();
+ envelope.put(discriminatorProperty, eventType);
+ envelope.put("data", parsedData);
+ if (id != null) {
+ envelope.put("id", id);
+ }
+ if (retry != null) {
+ envelope.put("retry", retry);
+ }
+
+ // Serialize to JSON and deserialize to target type
+ String envelopeJson = ObjectMappers.JSON_MAPPER.writeValueAsString(envelope);
+ return ObjectMappers.JSON_MAPPER.readValue(envelopeJson, unionClass);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to parse SSE event with event-level discrimination", e);
+ }
+ }
+
+ /**
+ * Parse an SSE event using data-level discrimination.
+ *
+ * Simply parses the data field as JSON and deserializes it to the target type.
+ * Jackson's polymorphic deserialization handles the discrimination automatically.
+ *
+ * @param data The SSE data content (from data: field)
+ * @param valueType The target type
+ * @param The target type
+ * @return The deserialized object
+ */
+ public static T parseDataLevelUnion(String data, Class valueType) {
+ try {
+ return ObjectMappers.JSON_MAPPER.readValue(data, valueType);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to parse SSE data with data-level discrimination", e);
+ }
+ }
+
+ /**
+ * Determines if the given discriminator property indicates event-level discrimination.
+ * Event-level discrimination occurs when the discriminator is an SSE envelope field.
+ *
+ * @param discriminatorProperty The discriminator property name
+ * @return true if event-level discrimination, false otherwise
+ */
+ public static boolean isEventLevelDiscrimination(String discriminatorProperty) {
+ return SSE_ENVELOPE_FIELDS.contains(discriminatorProperty);
+ }
+
+ /**
+ * Attempts to find the discriminator property from the union class's Jackson annotations.
+ *
+ * @param unionClass The union class to inspect
+ * @return The discriminator property name, or empty if not found
+ */
+ public static Optional findDiscriminatorProperty(Class> unionClass) {
+ try {
+ // Look for JsonTypeInfo on the class itself
+ JsonTypeInfo typeInfo = unionClass.getAnnotation(JsonTypeInfo.class);
+ if (typeInfo != null && !typeInfo.property().isEmpty()) {
+ return Optional.of(typeInfo.property());
+ }
+
+ // Look for inner Value interface with JsonTypeInfo
+ for (Class> innerClass : unionClass.getDeclaredClasses()) {
+ typeInfo = innerClass.getAnnotation(JsonTypeInfo.class);
+ if (typeInfo != null && !typeInfo.property().isEmpty()) {
+ return Optional.of(typeInfo.property());
+ }
+ }
+ } catch (Exception e) {
+ // Ignore reflection errors
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Parse the data field based on what the matching variant expects.
+ * If the variant expects a String for its data field, returns the raw string.
+ * Otherwise, parses the data as JSON.
+ */
+ private static Object parseDataForVariant(
+ String eventType,
+ String data,
+ Class> unionClass,
+ String discriminatorProperty) {
+ if (data == null || data.isEmpty()) {
+ return data;
+ }
+
+ try {
+ // Try to find the variant class that matches this event type
+ Class> variantClass = findVariantClass(unionClass, eventType, discriminatorProperty);
+ if (variantClass != null) {
+ // Check if the variant expects a String for the data field
+ Field dataField = findField(variantClass, "data");
+ if (dataField != null && String.class.equals(dataField.getType())) {
+ // Variant expects String - return raw data
+ return data;
+ }
+ }
+
+ // Try to parse as JSON
+ return ObjectMappers.JSON_MAPPER.readValue(data, new TypeReference