Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.representer.Representer;

/**
Expand Down Expand Up @@ -153,9 +154,23 @@ private static Map<String, Object> parseYamlMetadata(String yamlContent) {
Object loaded;
try {
loaded = createParserYaml().load(yamlContent);
} catch (RuntimeException e) {
logger.debug("Failed to parse YAML frontmatter, returning empty metadata", e);
return Map.of();
} catch (YAMLException ye) {
String repaired = repairYamlWithUnquotedColons(yamlContent);
if (!repaired.equals(yamlContent)) {
try {
loaded = createParserYaml().load(repaired);
logger.warn(
"YAML frontmatter contained unquoted colons and was auto-repaired. "
+ "Consider quoting scalar values containing ': ': {}",
yamlContent.substring(0, Math.min(80, yamlContent.length())));
} catch (RuntimeException re) {
logger.debug("Failed to repair YAML frontmatter, returning empty metadata", re);
return Map.of();
}
} else {
logger.debug("Failed to parse YAML frontmatter, returning empty metadata", ye);
return Map.of();
}
}

if (loaded == null) {
Expand All @@ -182,6 +197,97 @@ private static Map<String, Object> parseYamlMetadata(String yamlContent) {
return metadata;
}

/**
* Attempts to repair YAML content that contains unquoted colons in scalar values.
*
* <p>This handles the common case where a value contains patterns like "key:" that YAML
* interprets as mapping keys, for example:
* <pre>
* description: test, node: cannot find EDI partner
* </pre>
*
* <p>The repair strategy wraps values in double quotes when they contain ": " patterns
* that would otherwise be parsed as key-value separators.
*
* @param yamlContent The original YAML content that failed to parse
* @return Repaired YAML content, or the original if no repair was possible
*/
private static String repairYamlWithUnquotedColons(String yamlContent) {
StringBuilder result = new StringBuilder();
String[] lines = yamlContent.split("\n", -1);

for (String line : lines) {
int firstColon = line.indexOf(':');
if (firstColon > 0 && line.length() > firstColon + 1) {
String keyPart = line.substring(0, firstColon);
String valuePart = line.substring(firstColon + 1);

String trimmedKey = keyPart.trim();
if (!trimmedKey.isEmpty() && !trimmedKey.contains(" ") && needsQuoting(valuePart)) {
String repairedValue = quoteValue(valuePart);
line = keyPart + ":" + repairedValue;
}
}
result.append(line).append('\n');
}

if (!result.isEmpty()) {
result.setLength(result.length() - 1);
}
return result.toString();
}

/**
* Checks if a YAML value needs quoting because it contains unquoted colon-space patterns.
*/
private static boolean needsQuoting(String value) {
String trimmed = value.trim();
if (trimmed.isEmpty()) {
return false;
}

if ((trimmed.startsWith("\"") && trimmed.endsWith("\""))
|| (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
return false;
}

return findUnquotedColonSpace(trimmed) >= 0;
}

/**
* Finds the index of ": " that is not inside quotes.
*
* @return Index of the unquoted ": " or -1 if none found
*/
private static int findUnquotedColonSpace(String value) {
boolean inDoubleQuotes = false;
boolean inSingleQuotes = false;

for (int i = 0; i < value.length() - 1; i++) {
char c = value.charAt(i);
if (c == '"' && !inSingleQuotes) {
inDoubleQuotes = !inDoubleQuotes;
} else if (c == '\'' && !inDoubleQuotes) {
inSingleQuotes = !inSingleQuotes;
} else if (!inDoubleQuotes
&& !inSingleQuotes
&& c == ':'
&& value.charAt(i + 1) == ' ') {
return i;
}
}
return -1;
}

/**
* Quotes a YAML value in double quotes, escaping any internal double quotes and backslashes.
*/
private static String quoteValue(String value) {
String trimmed = value.trim();
String escaped = trimmed.replace("\\", "\\\\").replace("\"", "\\\"");
return " \"" + escaped + "\"";
}

private static LoaderOptions createLoaderOptions() {
LoaderOptions options = new LoaderOptions();
options.setAllowDuplicateKeys(false);
Expand Down
Loading
Loading