Csproj Analyzer & Cleaner
Paste a .csproj file to find duplicate references, redundant SDK-default properties, legacy elements, and XML errors, then copy a cleaned version.
How to use this tool
- Optionally enter your project's file name to enable the AssemblyName/RootNamespace check.
- Paste your .csproj content.
- Click Analyze (or Ctrl+Enter).
- Review each flagged category, then copy the cleaned .csproj from the output panel.
What this tool checks
Paste a .csproj file and this tool parses it with System.Xml.Linq (no external MSBuild or NuGet library) and flags four things: duplicate PackageReference entries (same package listed twice under the same condition — usually a copy-paste accident), properties that already match the SDK's own default (explicitly writing a value the SDK would apply anyway), legacy non-SDK-style elements left over from a pre-2017 project file, and malformed XML with the exact line and column.
A field note: the .csproj files that quietly cause trouble are the ones nobody's touched since the project was first scaffolded — years of copy-pasted PackageReference blocks from other projects, properties set to a value the SDK already defaults to, and the occasional pre-2017 leftover nobody noticed was still importing an old MSBuild target. None of that breaks the build. It just makes every future diff noisier and every dependency upgrade a little more error-prone, since a duplicated reference quietly obscures which version MSBuild actually resolves.
Properties this tool considers redundant
Only checked inside unconditional <PropertyGroup> blocks — a property set inside a Condition="..." block is deliberately overriding something for that specific configuration or target framework, so it's never flagged even if its value happens to match the default: OutputType=Library, GenerateAssemblyInfo=true, ImplicitUsings=disable, Nullable=disable, IsPackable=true, GenerateDocumentationFile=false, Deterministic=true, LangVersion=latest. AssemblyName/RootNamespace matching the project file name are only checked if you fill in the optional file name field above — there's no way to know your actual file name from pasted text alone, so this tool doesn't guess it.
Duplicate detection and multi-targeting
A PackageReference is only flagged as a duplicate if another one with the same package ID and the same ItemGroup condition already exists. The same package listed once under Condition="'$(TargetFramework)'=='net8.0'" and again under a different target framework condition is a completely normal multi-targeting pattern, not a duplicate — this tool doesn't flag that.
Worked example
Pasting a .csproj with a duplicated Newtonsoft.Json reference and a redundant <OutputType>Library</OutputType>:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
flags the duplicate reference and the redundant OutputType, and produces this cleaned output:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
Common errors and edge cases
| Message / behavior | Cause | Fix |
|---|---|---|
| "Paste a .csproj file to analyze." | Input is empty or whitespace-only | Paste your .csproj content into the editor |
| "Malformed XML at line X, column Y: ..." | Invalid XML syntax — an unclosed tag, a stray &, or a mismatched quote | XDocument.Parse reports the exact location; fix that line first |
| "Expected a <Project> root element..." | The pasted text doesn't have <Project> as its root — often a partial fragment copied from the middle of the file | Paste the whole file, starting from <Project Sdk="..."> |
| "Nothing to flag" chip with no alerts below it | This isn't an error — the file genuinely has no duplicate references, no redundant properties, and no legacy elements | Nothing to do; that's the clean-file result |
.csproj content
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>Paste a .csproj file and click Analyze.