Search code examples
c#linqproj

Why XDocument isn't reading elements value?


I have code that reads proj files and checks for their assembly names.

XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
XDocument projDefinition = XDocument.Load(projPath);
          assemblyName = projDefinition
          .Element(msbuild + "Project")
          .Element(msbuild + "PropertyGroup")
          .Element(msbuild + "AssemblyName")
          .Value;

Above code works perfect in 99% of the time. Today it got Null Object Reference Exception when it tried to get assembly name from below code. The top property group element and import element are usually towards the bottom of proj file.

My question is why XDocument is not going over Import Element and not picking up other propertygroup elements?

<PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <UseGlobalApplicationHostFile />
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>


Some Elements ...

<AssemblyName>AssemblyNameGoesHere</AssemblyName>

Solution

  • Based on the XML fragment you provided, I believe that the base of the issue is that your XML query is finding <PropertyGroup> elements that do not contain a child <AssemblyName> element, thus your NULL reference exception. What you might be after is code to gather all the <PropertyGroup> elements, loop through them looking for an <AssemblyName> element and return the first value you find for it.

    XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
    XDocument projDefinition = XDocument.Load(@"C:\Path\To\Project.csproj");
    
    var propertyGroups = projDefinition.Element(msbuild + "Project")
        .Elements(msbuild + "PropertyGroup");
    
    string assemblyNameValue = "";
    
    foreach (XElement propertyGroup in propertyGroups)
    {
        //Check if this <PropertyGroup> elements contains a <AssemblyName> element
        if (propertyGroup.Element(msbuild + "AssemblyName") != null)
        {
            assemblyNameValue = propertyGroup.Element(msbuild + "AssemblyName").Value;
            break;
        }
    }
    
    Console.WriteLine("AssemblyName: " + assemblyNameValue);