package viral import ( "encoding/json" "strings" ) func firstJSONString(obj map[string]json.RawMessage, keys ...string) string { for _, key := range keys { raw, ok := obj[key] if !ok { continue } var value string if err := json.Unmarshal(raw, &value); err == nil { if trimmed := strings.TrimSpace(value); trimmed != "" { return trimmed } } } return "" } func flexibleStringFromItem(raw json.RawMessage) string { if len(raw) == 0 { return "" } var value string if err := json.Unmarshal(raw, &value); err == nil { return strings.TrimSpace(value) } var obj map[string]json.RawMessage if err := json.Unmarshal(raw, &obj); err != nil { return "" } if line := firstJSONString(obj, "title", "name", "label", "pillar", "tag", "text", "summary", "description", "value", "content"); line != "" { return line } parts := []string{} for _, key := range []string{"title", "name", "label"} { if part := firstJSONString(obj, key); part != "" { parts = append(parts, part) break } } if detail := firstJSONString(obj, "description", "summary", "detail", "reason"); detail != "" { parts = append(parts, detail) } return strings.TrimSpace(strings.Join(parts, ":")) } func parseFlexibleStringList(raw json.RawMessage) []string { if len(raw) == 0 || string(raw) == "null" { return nil } var items []string if err := json.Unmarshal(raw, &items); err == nil { return cleanLines(items) } var single string if err := json.Unmarshal(raw, &single); err == nil && strings.TrimSpace(single) != "" { return cleanLines([]string{single}) } var arr []json.RawMessage if err := json.Unmarshal(raw, &arr); err != nil { return nil } out := make([]string, 0, len(arr)) for _, item := range arr { if line := flexibleStringFromItem(item); line != "" { out = append(out, line) } } return cleanLines(out) } func parseFlexibleSuggestedTags(raw json.RawMessage) []SuggestedTag { if len(raw) == 0 || string(raw) == "null" { return nil } var tags []SuggestedTag if err := json.Unmarshal(raw, &tags); err == nil && len(tags) > 0 { return cleanSuggestedTags(tags) } var strs []string if err := json.Unmarshal(raw, &strs); err == nil { out := make([]SuggestedTag, 0, len(strs)) for _, item := range strs { item = strings.TrimSpace(item) if item != "" { out = append(out, SuggestedTag{Tag: item}) } } return cleanSuggestedTags(out) } var arr []json.RawMessage if err := json.Unmarshal(raw, &arr); err != nil { return nil } out := make([]SuggestedTag, 0, len(arr)) for _, item := range arr { if tag := decodeSuggestedTagItem(item); tag.Tag != "" { out = append(out, tag) } } return cleanSuggestedTags(out) } func decodeSuggestedTagItem(raw json.RawMessage) SuggestedTag { var tag SuggestedTag if err := json.Unmarshal(raw, &tag); err == nil && strings.TrimSpace(tag.Tag) != "" { return tag } var snake struct { Tag string `json:"tag"` Reason string `json:"reason"` SearchIntent string `json:"search_intent"` SearchType string `json:"search_type"` } if err := json.Unmarshal(raw, &snake); err == nil && strings.TrimSpace(snake.Tag) != "" { return SuggestedTag{ Tag: strings.TrimSpace(snake.Tag), Reason: strings.TrimSpace(snake.Reason), SearchIntent: strings.TrimSpace(snake.SearchIntent), SearchType: strings.TrimSpace(snake.SearchType), } } if line := flexibleStringFromItem(raw); line != "" { return SuggestedTag{Tag: line} } return SuggestedTag{} } func pickRawMessage(root map[string]json.RawMessage, keys ...string) json.RawMessage { for _, key := range keys { if raw, ok := root[key]; ok && len(raw) > 0 && string(raw) != "null" { return raw } } return nil }