Mads Ravn Mads Ravn - 2 months ago 5
C# Question

Parsing "Static Analysis Results Interchange Format (SARIF)" in MSBuild

When running various analyzers against a project using MSBuild all failures will be output in "Static Analysis Results Interchange Format (SARIF)" format (see eg. For instance a build may yield the following

"version": "0.1",
"toolInfo": {
"toolName": "Microsoft (R) Visual C# Compiler",
"productVersion": "1.1.0",
"fileVersion": "1.1.0"
"issues": [
"ruleId": "SA1401",
"locations": [
"analysisTarget": [
"uri": "C:\\SomeFile.cs",
"region": {
"startLine": 708,
"startColumn": 30,
"endLine": 708,
"endColumn": 36
"shortMessage": "Field must be private",
"fullMessage": "A field within a C# class has an access modifier other than private.",
"properties": {
"severity": "Warning",
"warningLevel": "1",
"defaultSeverity": "Warning",
"title": "Fields must be private",
"category": "StyleCop.CSharp.MaintainabilityRules",
"helpLink": "https:\/\/\/DotNetAnalyzers\/StyleCopAnalyzers\/blob\/master\/documentation\/",
"isEnabledByDefault": "True",
"isSuppressedInSource": "True"

Now I would like to be able to parse the data above in the simplest way possible (and break the build if any non-suppressed issues are encountered). How to go about doing this?

PS. Preferably I would also like to avoid implementing my own MSBuild tasks and installing specific software (eg. PowerShell 3.0 - ConvertFrom-Json).


Since there apparently is no built-in way of doing this I ended up using an inline msbuild task (, defined as

<UsingTask TaskName="ParseUnsupressedAnalysisIssues" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <Files ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
        <Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
        <Reference Include="System.Runtime.Serialization" />
        <Reference Include="System.Xml" />
        <Reference Include="System.Xml.Linq" />
        <Using Namespace="System"/>
        <Using Namespace="System.Collections.Generic"/>
        <Using Namespace="System.IO"/>
        <Using Namespace="System.Linq"/>
        <Using Namespace="System.Runtime.Serialization.Json"/>
        <Using Namespace="System.Xml"/>
        <Using Namespace="System.Xml.Linq"/>
        <Code Type="Fragment" Language="cs">
            List<TaskItem> taskItems = new List<TaskItem>();
            foreach(ITaskItem item in Files)
                    string path = item.GetMetadata("FullPath");
                    using (FileStream fs = new FileStream(path, FileMode.Open))
                    using (XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(fs, XmlDictionaryReaderQuotas.Max))
                        XElement doc = XElement.Load(reader);
                        XElement issuesRoot = doc.Elements("issues").SingleOrDefault();
                        List<XElement> unsupressedIssues = issuesRoot.Elements("item").Where(e => !"True".Equals((string)e.Element("properties").Element("isSuppressedInSource"), StringComparison.Ordinal)).ToList();
                        string unsupressedIssuesString = string.Join(Environment.NewLine, unsupressedIssues);
                            taskItems.Add(new TaskItem(item.ItemSpec));
                catch(Exception e)
                    taskItems.Add(new TaskItem(item.ItemSpec));

            Result = taskItems.ToArray();

which can then be invoked as

<ParseUnsupressedAnalysisIssues Files="@(AnalyzerFiles)">
    <Output ItemName="FailedAnalyzerFiles" TaskParameter="Result" />
<Error Text="FxCopAll: Following assemblies had analyzer errors @(FailedAnalyzerFiles)" Condition="'@(FailedAnalyzerFiles->Count())' &gt; 0" Code="2"/>