A .NET library for validating HTML tag structure and reporting parse errors with line/column positions.
Supported target frameworks: net8.0, net9.0, net10.0.
- Validates start/end tag matching and nesting order
- Detects missing start or end tags
- Parses attributes and detects malformed attribute syntax
- Handles HTML comments (
<!-- ... -->) and DOCTYPE (<!DOCTYPE ...>) - Handles raw-content tags (
<script>,<style>) without false parsing on<inside content - Validates HTML void-element rules with strict behavior
- Returns structured error details (
ErrorCode,LineNumber,Position)
git clone https://github.com/foman/TagChecker.git
cd TagChecker
dotnet restore TagChecker.sln
dotnet build TagChecker.sln -c Release
dotnet test TagChecker.sln -c Releasedotnet add package TagCheckerOr in .csproj:
<ItemGroup>
<PackageReference Include="TagChecker" Version="1.0.0" />
</ItemGroup>using TagChecker;
var html = "<html><body><div class='x'>Hello</div></body></html>";
var (ok, error) = HtmlTagChecker.Validate(html);
if (ok)
{
Console.WriteLine("HTML is valid");
}
else
{
Console.WriteLine($"Error: {error.ErrorCode} at line {error.LineNumber}, col {error.Position}");
}Returns:
(bool succeed, TagError tagError)succeed:truewhen validation passestagError:nullon success; populated on failure
new ParserOptions
{
TagNamePatternStrict = true
}TagNamePatternStrict = false(default): compatibility modeTagNamePatternStrict = true: enforces strict tag-name pattern:- first character must be a letter (
A-Z/a-z) - remaining characters can be letters, digits, or hyphen (
-)
- first character must be a letter (
TagChecker currently uses strict void-element behavior:
<img />is valid<img>triggersVoidElementNotSelfClosed(strict XML-like rule)</img>triggersVoidElementClosed
Void-element set:
area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr
For <script> and <style>, content is treated as raw text until matching closing tags. This avoids incorrect parsing when the content contains < characters.
TagNotClosed: Missing closing>for a tagNoStartTagFound: Closing tag has no matching openerNoEndTagFound: Opening tag has no matching closerInvalidTag: Invalid tag structureInvalidTagName: Invalid tag nameInValidCloseTag: Invalid closing tag formatInvalidAttribute: Invalid attribute syntaxInvalidAttributeName: Invalid attribute nameAttributeNotClosed: Attribute value quote not closedVoidElementClosed: Void element incorrectly closed using</...>VoidElementNotSelfClosed: Void element not self-closed
var (ok, err) = HtmlTagChecker.Validate("<div><span>Hello</span></div>");
// ok == true
// err == nullvar (ok, err) = HtmlTagChecker.Validate("<div><span>Hello</div>");
// ok == false
// err.ErrorCode == HtmlParseErrorCode.NoStartTagFound (mismatched close)var options = new ParserOptions { TagNamePatternStrict = true };
var (ok, err) = HtmlTagChecker.Validate("<1bad></1bad>", options);
// ok == false
// err.ErrorCode == HtmlParseErrorCode.InvalidTagNameUse the lightweight helper script:
# Dry run (test + pack only)
./scripts/nuget-publish.sh --version 0.2.0 --dry-run
# Real publish
NUGET_API_KEY=*** ./scripts/nuget-publish.sh --version 0.2.0Optional flags:
--skip-tests: skip unit tests before packing--source <url>: custom NuGet source
publish.yaml is configured to publish when a GitHub Release is published.
- Trigger:
release.published - Version source: release tag (e.g.
v0.2.0-> package version0.2.0) - Required secret:
NUGET_API_KEY
Quick flow:
- Push tag, e.g.
v0.2.0 - Create a GitHub Release for that tag
- GitHub Actions runs build/test/pack/publish automatically
- Add parser modes: strict (current) vs lenient HTML5 void-element behavior
- Improve diagnostics with source snippets around error locations
- Add performance benchmarks for large files and deep nesting