C# XDocument.Validate未捕获XSD的所有错误
我在使用C#XDocument.Validate或具有所需配置的XMLReaderSettings根据有效的XSD验证XML文档时遇到了一个非常奇怪的问题。问题是:当XML文档中存在错误时,验证过程在某些条件下无法捕获所有错误,我无法找到这种异常的模式 这是我的XSD:C# XDocument.Validate未捕获XSD的所有错误,c#,xml,xsd,C#,Xml,Xsd,我在使用C#XDocument.Validate或具有所需配置的XMLReaderSettings根据有效的XSD验证XML文档时遇到了一个非常奇怪的问题。问题是:当XML文档中存在错误时,验证过程在某些条件下无法捕获所有错误,我无法找到这种异常的模式 这是我的XSD: 似乎XmlReader在遇到第一个错误时停止元素验证。这里有一个链接,指向对旧(过时)的xmlvalidateingreader的描述: 如果一个元素报告了一个验证错误,其余的内容 该元素的模型未验证,但其子元素已验证 验证。
似乎
XmlReader
在遇到第一个错误时停止元素验证。这里有一个链接,指向对旧(过时)的xmlvalidateingreader
的描述:
如果一个元素报告了一个验证错误,其余的内容
该元素的模型未验证,但其子元素已验证
验证。读卡器仅报告给定错误的第一个错误
元素
对于常规的XmlReader
(尽管文档中没有明确提到它),情况似乎也是如此
在第一个示例中,错误要么在最里面的元素(例如元素的无效文本值)中,要么在最后一个子元素中,因此它们都被报告,并且没有跳过任何内容。但是,在上一个示例中,在根
Abc001
元素的开头引入了错误,因此跳过了Abc001
内容的其余部分以及所有错误。我编写了这段代码,它验证xml消息的每个元素,并尝试更正可以更正的内容:
static void ValidateMessage(XDocument xDoc)
{
var schemas = new XmlSchemaSet();
schemas.Add("", @"Messages_Schema.xsd");
schemas.Compile();
var schemaElements = (XmlSchemaElement)schemas.GlobalElements.Values.OfType<XmlSchemaObject>()
.FirstOrDefault(e => ((XmlSchemaElement)e).Name == xDoc.Root?.Name.LocalName);
var xmlElement =
(XmlSchemaSequence)((XmlSchemaComplexType)schemaElements?.ElementSchemaType)?.ContentTypeParticle;
var elementNameList = new Dictionary<string, XmlSchemaObject>();
AddElementsInDictionary(xmlElement?.Items.OfType<XmlSchemaObject>().ToList(), elementNameList, new List<string>());
var errorList = new List<string>();
bool elementFixed;
do
{
errorList.Clear();
ValidateElements(xDoc.Root?.Elements().ToList(), elementNameList, schemas, new List<string>(), errorList);
errorList.ForEach(e =>
{
if (!ErrorLogs.Contains(e))
ErrorLogs.Add(e);
});
elementFixed = false;
if (XElementsToDelete.Any() || XElementsToCut.Any() || XElementsInvalid.Any())
{
XElementsInvalid.ForEach(xElement =>
{
var xParent = xElement.Parent;
var xParentSchema =
(xParent?.GetSchemaInfo()?.SchemaType as XmlSchemaComplexType)?.ContentTypeParticle as
XmlSchemaSequence;
var elementNameLists = new List<string>();
AddElementsInList(xParentSchema?.Items.OfType<XmlSchemaObject>().ToList(), elementNameLists);
var index = elementNameLists.IndexOf(xElement.Name.LocalName);
if (index == 0)
{
xElement.Remove();
var log = $" Move: {xElement.Name} element to the top of the sequence";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
elementFixed = true;
xParent?.AddFirst(xElement);
}
else
{
var xNextElement = xElement.NextNode as XElement;
var indexNext = elementNameLists.IndexOf(xNextElement?.Name.LocalName);
if (indexNext - index > 1)
{
var xPreviousElement = xElement.PreviousNode as XElement;
var indexPrevious = elementNameLists.IndexOf(xPreviousElement?.Name.LocalName);
do
{
indexPrevious -= 1;
var xLastValidElement = xParent?.Element(elementNameLists[indexPrevious]);
if (xLastValidElement == null) continue;
xPreviousElement?.Remove();
var log = $" Move: {xPreviousElement?.Name} element after {xLastValidElement.Name}";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
elementFixed = true;
xLastValidElement.AddAfterSelf(xPreviousElement);
break;
} while (indexPrevious > 0);
}
else
{
do
{
index -= 1;
var xLastValidElement = xParent?.Element(elementNameLists[index]);
if (xLastValidElement == null) continue;
xElement.Remove();
var log = $" Move: {xElement.Name} element after {xLastValidElement.Name}";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
elementFixed = true;
xLastValidElement.AddAfterSelf(xElement);
break;
} while (index > 0);
}
}
});
XElementsToDelete.ForEach(e =>
{
e.Remove();
var log = $" Delete: {e.Name} element";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
elementFixed = true;
});
XElementsToCut.ForEach(e =>
{
var schemaType = (XmlSchemaSimpleType)e.GetSchemaInfo()?.SchemaType;
var restriction = (XmlSchemaSimpleTypeRestriction)schemaType?.Content;
var enumFacets = restriction?.Facets.OfType<XmlSchemaMaxLengthFacet>();
var maxLengthFacet = enumFacets?.ToList().FirstOrDefault();
if (maxLengthFacet != null)
{
var maxLength = int.Parse(maxLengthFacet.Value);
var log = $" Cut: {e.Name} value to maxLength: {maxLength}";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
elementFixed = true;
e.Value = e.Value.Substring(0, maxLength);
}
});
if (!elementFixed)
{
var log = " Cannot fix:";
if (!FixLogs.Contains(log))
FixLogs.Add(log);
errorList.ForEach(e =>
{
if (!FixLogs.Contains($" {e}"))
FixLogs.Add($" {e}");
});
}
}
XElementsToDelete.Clear();
XElementsInvalid.Clear();
XElementsToCut.Clear();
} while (errorList.Count > 0 && elementFixed);
Console.WriteLine($"Validating a {xDoc.Root?.Name.LocalName}");
Console.WriteLine("");
ErrorLogs.ForEach(e =>
{
Console.WriteLine(" {0}", e);
});
Console.WriteLine("");
Console.WriteLine("Fixing");
Console.WriteLine("");
FixLogs.ForEach(e =>
{
Console.WriteLine("{0}", e);
});
Console.WriteLine("");
Console.WriteLine("Message {0}",
errorList.Count > 0 ? "did not validate" : "validated");
Console.ReadKey();
}
private static void AddElementsInList(IEnumerable<XmlSchemaObject> schemaObjectList,
ICollection<string> schemaObjectKeys)
{
schemaObjectList.ToList().ForEach(se =>
{
switch (se.GetType().ToString())
{
case "System.Xml.Schema.XmlSchemaElement":
var element = se as XmlSchemaElement;
var name = (se as XmlSchemaElement)?.QualifiedName.Name;
if (!schemaObjectKeys.Contains(name))
schemaObjectKeys.Add(name);
var elementSchemaType = element?.ElementSchemaType;
if (elementSchemaType != null)
AddElementsInList(new List<XmlSchemaObject> { elementSchemaType }, schemaObjectKeys);
break;
case "System.Xml.Schema.XmlSchemaComplexType":
break;
case "System.Xml.Schema.XmlSchemaSequence":
var sequence = se as XmlSchemaSequence;
var sequenceItems = sequence?.Items.OfType<XmlSchemaObject>().ToList();
if (sequenceItems != null)
AddElementsInList(sequenceItems, schemaObjectKeys);
break;
case "System.Xml.Schema.XmlSchemaChoice":
var choice = se as XmlSchemaChoice;
var choiceItems = choice?.Items.OfType<XmlSchemaObject>().ToList();
if (choiceItems != null)
AddElementsInList(choiceItems, schemaObjectKeys);
break;
case "System.Xml.Schema.XmlSchemaGroupRef":
var group = se as XmlSchemaGroupRef;
var groupParticle = group?.Particle;
if (groupParticle != null)
AddElementsInList(new List<XmlSchemaObject> { groupParticle }, schemaObjectKeys);
break;
}
});
}
private static void AddElementsInDictionary(IEnumerable<XmlSchemaObject> schemaObjectList,
IDictionary<string, XmlSchemaObject> schemaObjectDictionary, IList<string> schemaObjectDictionaryKeys)
{
schemaObjectList.ToList().ForEach(se =>
{
switch (se.GetType().ToString())
{
case "System.Xml.Schema.XmlSchemaElement":
var element = se as XmlSchemaElement;
schemaObjectDictionaryKeys.Add((se as XmlSchemaElement)?.QualifiedName.Name);
var path = string.Join("/", schemaObjectDictionaryKeys);
if (!schemaObjectDictionary.ContainsKey(path))
schemaObjectDictionary.Add(path, se);
var elementSchemaType = element?.ElementSchemaType;
if (elementSchemaType != null)
AddElementsInDictionary(new List<XmlSchemaObject> { elementSchemaType },
schemaObjectDictionary, schemaObjectDictionaryKeys);
if (schemaObjectDictionaryKeys.Count > 0)
schemaObjectDictionaryKeys.RemoveAt(schemaObjectDictionaryKeys.Count - 1);
break;
case "System.Xml.Schema.XmlSchemaComplexType":
var complexType = se as XmlSchemaComplexType;
var complexTypeParticle = complexType?.ContentTypeParticle;
if (complexTypeParticle != null)
AddElementsInDictionary(new List<XmlSchemaObject> { complexTypeParticle },
schemaObjectDictionary, schemaObjectDictionaryKeys);
break;
case "System.Xml.Schema.XmlSchemaSequence":
var sequence = se as XmlSchemaSequence;
var sequenceItems = sequence?.Items.OfType<XmlSchemaObject>().ToList();
if (sequenceItems != null)
AddElementsInDictionary(sequenceItems, schemaObjectDictionary, schemaObjectDictionaryKeys);
break;
case "System.Xml.Schema.XmlSchemaChoice":
var choice = se as XmlSchemaChoice;
var choiceItems = choice?.Items.OfType<XmlSchemaObject>().ToList();
if (choiceItems != null)
AddElementsInDictionary(choiceItems, schemaObjectDictionary, schemaObjectDictionaryKeys);
break;
case "System.Xml.Schema.XmlSchemaGroupRef":
var group = se as XmlSchemaGroupRef;
var groupParticle = group?.Particle;
if (groupParticle != null)
AddElementsInDictionary(new List<XmlSchemaObject> { groupParticle }, schemaObjectDictionary,
schemaObjectDictionaryKeys);
break;
}
});
}
private static void ValidateElements(List<XElement> xElementList,
IReadOnlyDictionary<string, XmlSchemaObject> schemaObjectDictionary, XmlSchemaSet schemas,
IList<string> schemaObjectDictionaryKeys, ICollection<string> errorList)
{
xElementList.ForEach(xElement =>
{
schemaObjectDictionaryKeys.Add(xElement.Name.LocalName);
var path = string.Join("/", schemaObjectDictionaryKeys);
if (schemaObjectDictionary.ContainsKey(path))
{
var validateObject = schemaObjectDictionary[path];
xElement.Validate(validateObject, schemas,
(o, e) =>
{
if (!errorList.Contains(e.Message))
errorList.Add(e.Message);
if (e.Message.Contains("has incomplete content"))
{
if (!XElementsToDelete.Contains((XElement)o))
XElementsToDelete.Add((XElement)o);
}
if (e.Message.Contains("has invalid child element"))
{
if (!XElementsInvalid.Contains((XElement)o))
XElementsInvalid.Add((XElement)o);
}
if (e.Message.Contains("actual length is greater than the MaxLength value"))
{
if (!XElementsToCut.Contains((XElement)o))
XElementsToCut.Add((XElement)o);
}
}, true);
if (xElement.HasElements)
ValidateElements(xElement.Elements().ToList(), schemaObjectDictionary, schemas,
schemaObjectDictionaryKeys, errorList);
}
else
{
var log = $"The element '{xElement.Name.LocalName}' is unknown. It should be delete.";
if (!errorList.Contains(log))
errorList.Add(log);
if (XElementsInvalid.Contains(xElement))
XElementsInvalid.Remove(xElement);
if (XElementsToCut.Contains(xElement))
XElementsToCut.Remove(xElement);
if (!XElementsToDelete.Contains(xElement))
XElementsToDelete.Add(xElement);
}
if (schemaObjectDictionaryKeys.Count > 0)
schemaObjectDictionaryKeys.RemoveAt(schemaObjectDictionaryKeys.Count - 1);
});
}
静态无效验证消息(XDocument xDoc)
{
var schemase=new XmlSchemaSet();
Schema.Add(“,@”Messages_Schema.xsd”);
schemas.Compile();
var schemaElements=(XmlSchemaElement)schemas.GlobalElements.Values.OfType()的值
.FirstOrDefault(e=>((XmlSchemaElement)e).Name==xDoc.Root?.Name.LocalName);
var xmlElement=
(XmlSchemaSequence)((XmlSchemaComplexType)schemaElements?.ElementSchemaType)?.ContentTypeParticle;
var elementNameList=新字典();
AddElementsInDictionary(xmlElement?.Items.OfType().ToList(),elementNameList,new List());
var errorList=新列表();
布尔元素固定;
做
{
errorList.Clear();
ValidateElements(xDoc.Root?.Elements().ToList(),elementNameList,Schema,new List(),errorList);
errorList.ForEach(e=>
{
如果(!ErrorLogs.Contains(e))
错误日志。添加(e);
});
elementFixed=false;
如果(XElementsToDelete.Any()| | XElementsToCut.Any()| | XElementsToDelete.Any())
{
XElementsInvalid.ForEach(xElement=>
{
var xParent=xElement.Parent;
var xParentSchema=
(xParent?.GetSchemaInfo()?.SchemaType为XmlSchemaComplexType)?.ContentTypeParticle为
XmlSchemaSequence;
var elementNameLists=新列表();
AddElementsInList(xParentSchema?.Items.OfType().ToList(),ElementNameList);
var index=elementNameLists.IndexOf(xElement.Name.LocalName);
如果(索引==0)
{
xElement.Remove();
var log=$“将{xElement.Name}元素移动到序列的顶部”;
如果(!FixLogs.Contains(log))
添加(日志);
elementFixed=true;
xParent?.AddFirst(xElement);
}
其他的
{
var xNextElement=xElement.NextNode作为xElement;
var indexNext=elementNameLists.IndexOf(xNextElement?.Name.LocalName);
如果(索引下一步-索引>1)
{
var xPreviousElement=xElement.PreviousNode作为xElement;
var indexPrevious=elementNameLists.IndexOf(xPreviousElement?.Name.LocalName);
做
{
indexPrevious-=1;
var xLastValidElement=xParent?.Element(elementNameList[indexPrevious]);
如果(xLastValidElement==null)继续;
xPreviousElement?.Remove();
var log=$“在{xLastValidElement.Name}之后移动:{xPreviousElement?.Name}元素”;
如果(!FixLogs.Contains(log))
添加(日志);
elementFixed=true;