Search code examples
c#.netxmlc#-4.0xmlreader

Unable to retrieve attribute names/values from XmlReader after validation in C#


I need to write a c# code to extract attribute info from an XML when the validation fails. The code I have below is able to give me the exact attribute that fails the validation but I need additional info from attributes present in the parent nodes as well.

XML:

<Payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlVersion="1.0" createDate="2023-11-07T14:23:08" messageType="TEST">
    <Orders>
        <OrderHeader resultsIndicator="F" orderNumber="2000100" orderStatus="FIN" latestShipDate="4/23/2023 12:00:00 AM" cusNumber="791053" cusName="Apple, Ms.Rose" deliveryTerms="HOME" paymentType="AMEX" Destination="TX">
            <OrderDetail resultsIndicator="F" lot="1234" sequence="01" invoice="FGD123401" finalDestination="75013" resultsMessage="DELIVERED" brandNumber="100141" brandName="LG">
<OrderCostingDetail expenseCode="ADMIN" expenseCategory="ADM" rate="2499.50" indicator="F" qualifier="SALE" unitCost="2499.50" level="002"/>
                <OrderCostingDetail expenseCode="ADMIN" expenseCategory="ADM" rate="125.00" indicator="F" qualifier="SALE" unitCost="" level="002"/>
                <OrderCostingDetail expenseCode="ADMIN" expenseCategory="ADM" rate="5879.20" indicator="F" qualifier="SALE" unitCost="5879.20" level="002"/>
            </OrderDetail>
        </OrderHeader>
        <OrderHeader resultsIndicator="I" orderNumber="2000101" orderStatus="INIT" latestShipDate="12/10/2023 12:00:00 AM" cusNumber="56782" cusName="Roy, Mr.Ralph" deliveryTerms="HOME" paymentType="VISA" Destination="TX">
            <OrderDetail resultsIndicator="I" lot="1234" sequence="01" invoice="FGD123401" finalDestination="75013" resultsMessage="DELIVERED" brandNumber="100141" brandName="LG">
                <OrderCostingDetail expenseCode="ADMIN" expenseCategory="ADM" rate="24.00" indicator="I" qualifier="SALE" unitCost="24.00" level="004"/>
            </OrderDetail>
        </OrderHeader>
    </Orders>
</Payload>

Code:

private static void PerformValidation(XmlTextReader xmlTextReader, XmlSchemaSet schemas)
{
    if (schemas == null)
    {
        throw new ArgumentNullException("schemas");
    }

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.Schemas = schemas;
    settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings; //sarul1
    // settings.ValidationEventHandler += (sender, args) => error = ValidationCallback(sender, args);

    string chkatt = string.Empty;

    using (XmlReader reader = XmlReader.Create(xmlTextReader, settings))
    {
        try
        {
            do
            {
                chkatt = string.Concat("orderNo.: ",reader.GetAttribute("orderNumber"), ", Lot:", reader.GetAttribute("lot"), ", Seq:", reader.GetAttribute("sequence"));
            } while (reader.Read());
        }
        catch(Exception ex)
        {
            string error = ex.Message.ToString() + chkatt;
        }
        finally
        {
            reader.Close();   
        }
    }
}

I call the above method in my main function to validate if there are any NULL fields, against a predefined schema. I get an error

The 'unitCost' attribute is invalid...The string '' is not a valid Decimal value

I need to add other attribute info like orderNumber, Lot and Sequence to this message (in this case: orderNumber = 2000100, Lot = 1234, Seq = 01) which I am not able to do with this code.

Please help me move forward!


Solution

  • The PerformValidation function lacked context in the error messages because it didn't keep track of attribute values in parent nodes.

    consider this updated code:

    private static void PerformValidation(XmlTextReader xmlTextReader, XmlSchemaSet schemas)
    

    { if (schemas == null) { throw new ArgumentNullException("schemas"); }

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.Schemas = schemas;
    settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
    
    string orderNumber = string.Empty;
    string lot = string.Empty;
    string sequence = string.Empty;
    
    using (XmlReader reader = XmlReader.Create(xmlTextReader, settings))
    {
        try
        {
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "OrderHeader")
                    {
                        orderNumber = reader.GetAttribute("orderNumber");
                    }
                    else if (reader.Name == "OrderDetail")
                    {
                        lot = reader.GetAttribute("lot");
                        sequence = reader.GetAttribute("sequence");
                    }
                }
                else if (reader.NodeType == XmlNodeType.Attribute && reader.Value == string.Empty)
                {
                    string errorMessage = $"Validation error in OrderNo.: {orderNumber}, Lot: {lot}, Seq: {sequence}. ";
                    
                    // Handle or log the error message as needed
                    Console.WriteLine(errorMessage);
                }
            }
        }
        catch (Exception ex)
        {
            string error = ex.Message.ToString();
            // Handle or log the exception as needed
            Console.WriteLine(error);
        }
        finally
        {
            reader.Close();
        }
    }
    

    }

    This code introduced variables (orderNumber, lot, and sequence) to store these values, allowing for more informative error messages when validation errors occurred.

    give it a try and let me know if further modifications are needed. and kindly don't forget to mark as an answer if this answer has solved your question. :)