Home > Software engineering >  What to add to the code so not breaking when XML fields do not exist
What to add to the code so not breaking when XML fields do not exist

Time:06-21

I am parsing an XML file in C#, how I can make sure the code is not breaking if XML does not have one of these fields I am parsing? I don't want to through an exception as well.

       StringBuilder output = new StringBuilder();

        var xDocument = XDocument.Load(filePath);
        string xml = xDocument.ToString();
        XDocument doc = XDocument.Parse(xml);
        var col = doc.Root.Elements("LineItems")
                     .Elements("LineItem")
                     .Elements("OrderLine")
                     .Select(e => new
                     {
                         PurchaseOrderNumber = doc.Root.Element("Header").Element("OrderHeader").Element("Purchase").Value,
                         InternalOrderNumber = doc.Root.Element("Header").Element("OrderHeader").Element("InternalOrderNumber").Value,
                         VendorPartNumber = e.Element("VendorPartNumber").Value,
                         ItemStatus = e.Element("ItemStatus").Value,
                         Date = e.Element("ExpectedDate").Value
                     });

For example, I removed ExpectedDate from XML and this is breaking. what can I add to this code that is not breaking when XML fields do not exist?

I added this and it works: ate = e.Element("ExpectedDate")?.Value,

But now if ExpectedDate is not existed this would be 1/1/0001 12:00:00 AM - How I can have an empty string so this line won't break:

  OrderLine orderline = new OrderLine() { ItemStatus = item.ItemStatus, VendorPartNumber = item.VendorPartNumber, Date = Convert.ToDateTime(item.Date) };

This is XML sample:

<Purchase>
    <Header>
    <OrderHeader>
        <TradingId>OPIE</TradingId>
    </OrderHeader>
</Header>
<LineItems>
    <LineItem>
        <OrderLine>
            <UnitPrice>1073.25</UnitPrice>
            <ExtendedLineAmount>1073.25</ExtendedLineAmount>
            <ItemStatus>Backorder</ItemStatus>
            <ExpectedDate>2022-05-25</ExpectedDate>
        </OrderLine>
    </LineItem>
    <LineItem>
        <OrderLine>
            <UnitPrice>292.410000</UnitPrice>
            <ExtendedLineAmount>584.82</ExtendedLineAmount>
            <ItemStatus>Released</ItemStatus>
            <ExpectedDate>2022-06-29</ExpectedDate>
        </OrderLine>
    </LineItem>

</LineItems>

CodePudding user response:

Please try the following solution.

I saved your XML as a "e:\temp\alma.xml" file. And intentionally commented out

<!--<ExpectedDate>2022-06-29</ExpectedDate>-->

A question mark is standard way in the .Net Framework way to handle missing values as nulls:

ExpectedDate = e.Element("ExpectedDate")?.Value

Commented out ?? "default value" should match an expected data type.

XML

<?xml version="1.0"?>
<Purchase>
    <Header>
        <OrderHeader>
            <TradingId>OPIE</TradingId>
        </OrderHeader>
    </Header>
    <LineItems>
        <LineItem>
            <OrderLine>
                <UnitPrice>1073.25</UnitPrice>
                <ExtendedLineAmount>1073.25</ExtendedLineAmount>
                <ItemStatus>Backorder</ItemStatus>
                <ExpectedDate>2022-05-25</ExpectedDate>
            </OrderLine>
        </LineItem>
        <LineItem>
            <OrderLine>
                <UnitPrice>292.410000</UnitPrice>
                <ExtendedLineAmount>584.82</ExtendedLineAmount>
                <ItemStatus>Released</ItemStatus>
                <!--<ExpectedDate>2022-06-29</ExpectedDate>-->
            </OrderLine>
        </LineItem>
    </LineItems>
</Purchase>

c#

void Main()
{
    const string filePath = @"e:\temp\alma.xml";
    
    XDocument doc = XDocument.Load(filePath);
    var col = doc.Descendants("OrderLine")
            .Select(e => new
            {
                UnitPrice = e.Element("UnitPrice").Value,
                ExtendedLineAmount = e.Element("ExtendedLineAmount").Value,
                ItemStatus = e.Element("ItemStatus").Value,
                ExpectedDate = e.Element("ExpectedDate")?.Value // ?? "default Value"
            });
    Console.WriteLine(col);
}

Output

 ------------ -------------------- ------------ -------------- 
| UnitPrice  | ExtendedLineAmount | ItemStatus | ExpectedDate |
 ------------ -------------------- ------------ -------------- 
|    1073.25 |            1073.25 | Backorder  | 2022-05-25   |
| 292.410000 |             584.82 | Released   | null         |
 ------------ -------------------- ------------ -------------- 

CodePudding user response:

What works for me is to make an extension for XElement that returns true if the element exists (and then supply a default value if it doesn't):

public static class Extensions
{
    public static bool TryGetElement(
        this XElement pxel, 
        string name, 
        out XElement xel)
    {
        xel = pxel.Element(name);
        return xel != null;
    }
}

Using it like this:

var doc = XDocument.Parse(source);
var col = doc.Root.Elements("LineItems")
    .Elements("LineItem")
    .Elements("OrderLine")
    .Select(e => new
    {
        PurchaseOrderNumber = doc.Root.Element("Header").Element("OrderHeader").Element("Purchase").Value,
        VendorPartNumber = e.Element("VendorPartNumber").Value,
        ItemStatus = e.Element("ItemStatus").Value,
        Date = e.TryGetElement("ExpectedDate", out XElement xel) ? 
            xel.Value : 
            String.Empty
    });

foreach (var orderline in col)
{
    Console.WriteLine(orderline.ToString());
}

TESTING

console output

Where test source XML (made pathological by removing the 2nd ExpectedDate) is:

const string source =
@"<Purchase>
    <Header>
        <OrderHeader>
            <Purchase>12345</Purchase>
        </OrderHeader>
    </Header>
    <LineItems>
        <LineItem>
            <OrderLine>
                <VendorPartNumber>1</VendorPartNumber>
                <UnitPrice>1073.25</UnitPrice>
                <ExtendedLineAmount>1073.25</ExtendedLineAmount>
                <ItemStatus>Backorder</ItemStatus>
                <ExpectedDate>2022-05-25</ExpectedDate>
            </OrderLine>
        </LineItem>
        <LineItem>
            <OrderLine>
                <VendorPartNumber>2</VendorPartNumber>
                <UnitPrice>292.410000</UnitPrice>
                <ExtendedLineAmount>584.82</ExtendedLineAmount>
                <ItemStatus>Released</ItemStatus>
            </OrderLine>
        </LineItem>
    </LineItems>
</Purchase>";
  • Related