Reputation: 337
I have xml that is sent by a third party and I want to validate it.
XElement xTree = XElement.Parse(@"<Container>
<TrackingReferences>
<TrackingReference>
<TrackingName>Donny</TrackingName>
<TrackingCodes>
<TrackingCode>
<Name></Name>
</TrackingCode>
<TrackingCode>
<Name>DisplayThis</Name>
</TrackingCode>
<TrackingCode>
<Name></Name>
</TrackingCode>
</TrackingCodes>
</TrackingReference>
</TrackingReferences>
</Container>");
IEnumerable<XElement> childList = xTree.Element("TrackingReferences").Descendants("TrackingReference").Where(
tr => (
tr.Element("TrackingName") != null && !tr.Element("TrackingName").IsEmpty && !String.IsNullOrEmpty(tr.Element("TrackingName").Value) &&
tr.Descendants("TrackingCodes").Any(
tc => tc.HasElements &&
tc.Elements("TrackingCode").Any(
code => code.Element("Name") != null && !code.Element("Name").IsEmpty && !String.IsNullOrEmpty(code.Element("Name").Value)
)
)
)
);
I can't figure out how to return the descendants that I would like.
The problem I have is that I only want the TrackingReference element to contain TrackingCode descendants when that TrackingCode has a Name element that isn't null or empty.
The below example returns:
<TrackingReference>
<TrackingName>Donny</TrackingName>
<TrackingCodes>
<TrackingCode>
<Name></Name>
</TrackingCode>
<TrackingCode>
<Name>DisplayThis</Name>
</TrackingCode>
<TrackingCode>
<Name></Name>
</TrackingCode>
</TrackingCodes>
</TrackingReference>
However in this example I don't want the first and third TrackingCode elements to be returned, just the second as this has a Name element with value, like this:
<TrackingReference>
<TrackingName>Donny</TrackingName>
<TrackingCodes>
<TrackingCode>
<Name>DisplayThis</Name>
</TrackingCode>
</TrackingCodes>
</TrackingReference>
This is the first time I've tried a LINQ query to XML so any advice on how to make the query more clean/efficient would be much appreciated, or if I'm going about this the wrong way.
Upvotes: 0
Views: 1386
Reputation: 107407
Just in case you did want to consider a xslt solution, you can eliminate the empty TrackingCode
s with the identity transform and a template to eat
empty nodes:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TrackingCode[not(Name)] | TrackingCode[Name='']"/>
</xsl:stylesheet>
Upvotes: 0
Reputation: 1504182
Okay, it sounds like you want the TrackingCode
elements rather than the TrackingReference
elements, so it's actually pretty easy:
var query = doc.Descendants("TrackingReference")
// TODO: Filter based on TrackingName if you want...
.Descendants("TrackingCode")
.Where(x => !string.IsNullOrEmpty((string) x.Element("Name"));
This uses the fact that the explicit string conversion on XElement
will return null
if you call it with a null
operand.
Upvotes: 2