我可以跳过SchemaValidation失败的节点吗?

时间:2014-03-17 14:21:55

标签: c# .net xml validation xsd-validation

我有验证支持违反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;
    }

0 个答案:

没有答案