Search code examples
c#xmlprintinglinq-to-xmlxelement

Accessing a sub attribute in a XML file using XElement


In C#, I am trying to change an option of a feature in an XML file which presents a Print Ticket and loaded as an XElement by the following code:

XElement ticketRootXElement = null;
using (Stream ticketReadStream = displayedPrintTicket.GetReadStream())
{
   ticketRootXElement = XElement.Load(ticketReadStream);
}

The partial XML is something like following:

<?xml version="1.0"?>
<psf:Feature xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework" name="psk:PageMediaSize">
    <psf:Option name="psk:ISOA4">
        <psf:ScoredProperty name="psk:MediaSizeWidth">
            <psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">210000</psf:Value>
        </psf:ScoredProperty>
        <psf:ScoredProperty name="psk:MediaSizeHeight">
            <psf:Value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:integer">297000</psf:Value>
        </psf:ScoredProperty>
    </psf:Option>
</psf:Feature>

How can I access the "Option" of a specific "Feature" and change it to something like
<psf:Option name="psk:ISOA3">?

I tried the following code, but it fails.

foreach (XAttribute xAttr in ticketRootXElement.Descendants(xns_psf + "Feature").Attributes())
{
   if (xAttr.Value.Equals("psk:PageMediaSize"))
   {
      foreach(XAttribute xSubAttr in ticketRootXElement.Element("PageMediaSize").Descendants(xns_psf + "Option").Attributes())
      {
         if (xAttr.NextAttribute.Name.LocalName.Equals("name"))
         {
            xAttr.NextAttribute.SetValue("psk:ISO" + cmb_PaperSize.SelectedValue.ToString());
         }
      }
   }
}

Solution

  • You can can modify the option value of your selected feature as as follows:

    var featureName = "psk:PageMediaSize";
    var newOptionValue = "psk:ISOA3"; // Your modified value here
    
    XNamespace xns_psf = @"http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework";
    var query = from f in ticketRootXElement.DescendantsAndSelf(xns_psf + "Feature")
                where (string)f.Attribute("name") == featureName
                select f;
    foreach (var f in query)
    {
        // TODO: handle the situation were a child <psf:Option> element is missing.
        f.Element(xns_psf + "Option").SetAttributeValue("name", newOptionValue);
    }
    

    Notes:

    • XElement.Attribute(XName) can be used to look up an attribute by name, and XElement.SetAttributeValue(XName, Object) can be used to set or add an attribute value by name.

    • Casting an XAttribute to a string returns the value of the attribute, or null if the attribute was missing, and so is convenient to use when filtering by attribute value in a where statement.

    • If the selected <psf:Feature> element does not have a child <psf:Option> element, the above code will throw an exception. You will need to check your XML schema to determine whether this is possible, and if so, how to handle it.

    Demo fiddle here.