我有验证支持违反xsd-schema中定义的限制的节点的错误,这非常有用。我的问题是所有节点(包括导致验证错误的节点)都被传递给我的数据导入。是否无法配置XmlReaderSettings
节点导致验证错误被跳过,或者提取仅包含有效节点的集合?
目前我有一个临时解决方案,我将每个节点与整个错误集合进行比较,以查看错误是否描述了与节点对应的属性/值,如果是,则忽略节点 - 这显然是一个可怕的解。或者,xsd-schema中的验证规则应该在代码中实现并手动维护 - 在这种情况下,xsd-schema的大部分值都将丢失。
我认为显而易见的地方是寻找一个" sourceNode" -ish对象在ValidationEventHandler
参数中,但显然不是。
我正在使用System.Xml.dll程序集,运行时版本4.0.30319
XmlSerializer的
public class XmlSerializer<T> where T : class
{
private readonly ThreadLocal<List<XmlValidationError>> _errors;
public string Xml { get; private set; }
public XmlReaderSettings XmlReaderSettings { get; private set; }
public XmlSerializer(string xml, string xsdFilePath)
{
_errors = new ThreadLocal<List<XmlValidationError>>(() => new List<XmlValidationError>());
Xml = xml;
XmlReaderSettings = new XmlReaderSettings();
XmlReaderSettings.Schemas.Add("", xsdFilePath);
XmlReaderSettings.ValidationEventHandler += validationEventHandler;
XmlReaderSettings.ValidationType = ValidationType.Schema;
XmlReaderSettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
}
private void validationEventHandler(object sender, ValidationEventArgs validationEventArgs)
{
_errors.Value.Add(new XmlValidationError(Xml, validationEventArgs.Message, validationEventArgs.Exception));
}
private void Error(Exception ex)
{
_errors.Value.Add(new XmlValidationError(Xml, ex.Message, ex));
}
public T Deserialize(out List<XmlValidationError> errors)
{
_errors.Value.Clear();
T result = (null);
try
{
using (var fs = new FileStream(Xml, FileMode.Open, FileAccess.Read))
{
using (var reader = XmlReader.Create(fs, XmlReaderSettings))
{
var xmlSerializer = new XmlSerializer(typeof(T));
result = (T)xmlSerializer.Deserialize(reader);
}
}
}
catch (Exception ex)
{
_errors.Value.Add(new XmlValidationError(Xml, ex.Message, ex));
}
errors = new List<XmlValidationError>(_errors.Value);
return result;
}
public List<XmlValidationError> Validate(T instance)
{
_errors.Value.Clear();
using (var memstream = new MemoryStream())
{
Validate(instance, memstream);
}
return new List<XmlValidationError>(_errors.Value);
}
private void Validate(T instance, Stream stream)
{
if (Serialize(stream, instance))
{
try
{
stream.Seek(0, SeekOrigin.Begin);
using (var reader = XmlReader.Create(stream, XmlReaderSettings))
{
while (reader.Read())
{
}
}
}
catch (Exception ex)
{
Error(ex);
}
}
}
.... Save/Serialize code omitted
}
XSD架构
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Artec-availibility">
<xsd:annotation>
<xsd:documentation>Stock feed</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Artikel" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>Record describing stock information for a product</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ArtecArtikelNr">
<xsd:annotation>
<xsd:documentation>ProductId - must be min. 8 and max. 9 characters, of '0' through '9'</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="^[0-9]*$" />
<xsd:minLength value="8"/>
<xsd:maxLength value="9"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="AvailabilityState">
<xsd:annotation>
<xsd:documentation>StockAvailability</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:int">
<xsd:minInclusive value="0"/>
<xsd:maxInclusive value="2"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
库存可用性导入 - 作业
....
public bool ImportStockFromXmlFile(string xmlFile)
{
bool anyWorkDone = false;
string path = _fileService.StockFolderPath + "\\" + xmlFile;
string xsdPath = _fileService.SchemasFolderPath + "\\" + "Artec-Availability.xsd";
var serializer = new XmlSerializer<Artecavailibility>(path, xsdPath);
List<XmlValidationError> errors;
var feed = serializer.Deserialize(out errors);
foreach (var xmlValidationError in errors)
{
_importLog.AddXMLErrorMessage(xmlValidationError.File, xmlValidationError.Message, _context);
}
if (feed == null)
{
return false;
}
var stocks = new List<ImportStockDto>();
foreach (var stock in feed.Artikel)
{
//todo: Refactor once decided - this current solution will not scale well!
//If errors exist for related properties, skip this node
//Check articleNr
if(errors.Any(e => e.Message.Contains("ArtecArtikelNr") && e.Message.Contains(stock.ArtecArtikelNr)))
continue;
//Check
if (errors.Any(e => e.Message.Contains("AvailabilityState") && e.Message.Contains(stock.AvailabilityState.ToString())))
continue;
//Good to go
anyWorkDone = true;
int productId = int.Parse(stock.ArtecArtikelNr);
stocks.Add(new ImportStockDto()
{
Id = productId,
Stock = stock.AvailabilityState
});
}
try
{
_stockImportService.ImportStock(stocks);
for (int idx = 0; idx < stocks.Count; ++idx )
{
_importLog.AddOverwriteMessage(path, ImportLog.ImportContext.Stock);
}
}
catch (Exception e)
{
//-- Failed insert products into DB
_importLog.AddXMLErrorMessage(xmlFile, e.Message, _context);
}
return anyWorkDone;
}