Search code examples
c#xmlxelement

Parsing vcxproj with XElement - unable to retrieve inner Import


I am trying to parse a vcxproj file, using - any method I can (I have tried XPathDocument, XElement, XDocument... nothing works)

The typical project configuration:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="P">  ...  </ItemGroup>
  <ItemGroup>   <C I="..." />    </ItemGroup>
  <ItemGroup>   <C I="..." />    </ItemGroup>
  <ItemGroup>    ...  </ItemGroup>    
  <Import Project="aaaaa" />
  <PropertyGroup C="..." Label="...">  ... </PropertyGroup>  
  <Import Project="ooother" />
  <ImportGroup Label="E">  </ImportGroup>
  <ImportGroup Label="I_NEED_THIS" C="...">
    <Import Project="other" Condition="x" Label="L" />
    <Import Project="I_NEED_THIS_VALUE" />
  </ImportGroup>  
  <Import Project="bbbbb" />
  <ImportGroup Label="Ex">  </ImportGroup>
</Project>

I am trying to get the item(s) from inside the ImportGroup with the Label I_NEED_THIS, I would like to get all of them and be able to check (if they have) their Label, or Condition...

I suspected that the problem may be that there are multiple elements with similar names, so I tried to just get one level at a time,

XElement xmlTree = XElement.Load(projectPath);
XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
List<XElement> projectElements = (
    from mainElement in xmlTree.Descendants(ns + "Project")
    from subElement in mainElement.Elements()
    select subElement
).ToList();

if (projectElements.Count == 0)
  MessageBox.Show("Nothing is working today");

The above, to be followed with a few foreach loops...

foreach (XElement projectElement in projectElements)
{
List<XElement> importElements = (
   from mainElement in projectElement.Descendants(ns + "ImportGroup")
   from subElement in mainElement.Elements()
   select subElement
).ToList();
...
}

And so on, but when testing even the first loop, the Count of the projectElements was 0...

I have tried it without the namespace as well...

What am I missing ? Thank you...


Solution

  • You can get rid of those calls to Descendants. Calling Elements directly should be fine. This is how to achieve this using simple loops:

    // we can directly grab the namespace, it's better than hard-coding it
    XNamespace ns = xmlTree.Name.Namespace;
    // xmlTree itself is the Project element, just to make sure:
    Debug.Assert(xmlTree.Name.LocalName == "Project");
    
    // the following is all elements named "ImportGroup" under "Project"
    var importGroups = xmlTree.Elements(ns + "ImportGroup");
    foreach(XElement child in importGroups)
    {
        // the following are all "Import" elements under "ImportGroup" elements
        var imports = child.Elements(ns + "Import");
        foreach (var importElem in imports)
        {
            Console.WriteLine(importElem.Attribute("Project").Value);
        }
    }
    
    //This is the output:
    //other
    //I_NEED_THIS_VALUE
    

    Alternatively you can use the following code, which directly goes to the second element that contains the attribute valued "I_NEED_THIS_VALUE":

    var elems = xmlTree.Elements(ns + "ImportGroup")
        .Where(x => x.Attributes("Label").Any(xattr => xattr.Value == "I_NEED_THIS"))
            .Elements(ns + "Import")
            .Where(x => x.Attributes("Project").Any(xattr => xattr.Value == "I_NEED_THIS_VALUE"));