You've already forked DataMate
feat(annotation): 支持从XML配置解析标注任务模板
- 添加 XML 配置解析功能,支持从 Label Studio XML 格式提取 objects 和 labels - 优化模板配置加载逻辑,优先使用 configuration 字段,否则从 labelConfig 解析 - 增加对多种数据对象类型的解析支持(Image、Text、Audio 等) - 实现标签控件类型的完整解析(Choices、Labels、RectangleLabels 等)
This commit is contained in:
@@ -121,18 +121,29 @@ export default function CreateAnnotationTask({
|
||||
});
|
||||
setSelectedDatasetId(taskDetail.datasetId);
|
||||
|
||||
// 填充模板配置
|
||||
if (taskDetail.configuration) {
|
||||
const { objects, labels } = taskDetail.configuration;
|
||||
// 配置可能在 template.configuration 或 taskDetail.configuration
|
||||
const configuration = taskDetail.template?.configuration || taskDetail.configuration;
|
||||
const labelConfig = taskDetail.template?.labelConfig || taskDetail.labelConfig;
|
||||
|
||||
// 设置 XML 配置用于预览
|
||||
if (labelConfig) {
|
||||
setCustomXml(labelConfig);
|
||||
}
|
||||
|
||||
// 填充模板配置:优先使用 configuration,否则从 labelConfig 解析
|
||||
if (configuration && configuration.objects?.length > 0) {
|
||||
const { objects, labels } = configuration;
|
||||
manualForm.setFieldsValue({
|
||||
objects: objects || [],
|
||||
labels: labels || [],
|
||||
});
|
||||
}
|
||||
|
||||
// 设置 XML 配置用于预览
|
||||
if (taskDetail.labelConfig) {
|
||||
setCustomXml(taskDetail.labelConfig);
|
||||
} else if (labelConfig) {
|
||||
// 从 XML 解析配置
|
||||
const parsed = parseXmlToConfig(labelConfig);
|
||||
manualForm.setFieldsValue({
|
||||
objects: parsed.objects,
|
||||
labels: parsed.labels,
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑模式始终使用 custom 配置模式(不改变结构,只改标签)
|
||||
@@ -243,6 +254,80 @@ export default function CreateAnnotationTask({
|
||||
}
|
||||
};
|
||||
|
||||
// 从 Label Studio XML 配置解析出 objects 和 labels
|
||||
const parseXmlToConfig = (xml: string): { objects: any[], labels: any[] } => {
|
||||
const objects: any[] = [];
|
||||
const labels: any[] = [];
|
||||
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xml, "text/xml");
|
||||
|
||||
// 数据对象类型列表
|
||||
const objectTypes = ["Image", "Text", "Audio", "Video", "HyperText", "Header", "Paragraphs", "TimeSeries", "TimeSeriesChannel"];
|
||||
// 标签控件类型列表
|
||||
const controlTypes = ["Choices", "Labels", "RectangleLabels", "PolygonLabels", "EllipseLabels", "KeyPointLabels", "BrushLabels", "TextArea", "Number", "DateTime", "Rating", "Taxonomy"];
|
||||
|
||||
// 解析数据对象
|
||||
objectTypes.forEach(type => {
|
||||
const elements = doc.getElementsByTagName(type);
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const el = elements[i];
|
||||
const name = el.getAttribute("name") || "";
|
||||
const value = el.getAttribute("value") || "";
|
||||
if (name) {
|
||||
objects.push({ name, type, value });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 解析标签控件
|
||||
controlTypes.forEach(type => {
|
||||
const elements = doc.getElementsByTagName(type);
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const el = elements[i];
|
||||
const fromName = el.getAttribute("name") || "";
|
||||
const toName = el.getAttribute("toName") || "";
|
||||
const required = el.getAttribute("required") === "true";
|
||||
|
||||
if (fromName) {
|
||||
const label: any = {
|
||||
fromName,
|
||||
toName,
|
||||
type,
|
||||
required,
|
||||
};
|
||||
|
||||
// 解析选项/标签值
|
||||
if (type === "Choices") {
|
||||
const choices: string[] = [];
|
||||
const choiceElements = el.getElementsByTagName("Choice");
|
||||
for (let j = 0; j < choiceElements.length; j++) {
|
||||
const value = choiceElements[j].getAttribute("value");
|
||||
if (value) choices.push(value);
|
||||
}
|
||||
label.options = choices;
|
||||
} else if (["Labels", "RectangleLabels", "PolygonLabels", "EllipseLabels", "KeyPointLabels", "BrushLabels"].includes(type)) {
|
||||
const labelValues: string[] = [];
|
||||
const labelElements = el.getElementsByTagName("Label");
|
||||
for (let j = 0; j < labelElements.length; j++) {
|
||||
const value = labelElements[j].getAttribute("value");
|
||||
if (value) labelValues.push(value);
|
||||
}
|
||||
label.labels = labelValues;
|
||||
}
|
||||
|
||||
labels.push(label);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Failed to parse XML config:", e);
|
||||
}
|
||||
|
||||
return { objects, labels };
|
||||
};
|
||||
|
||||
const generateXmlFromConfig = (objects: any[], labels: any[]) => {
|
||||
let xml = '<View>\n';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user