GunsNPrices 0.1.0

dotnet add package GunsNPrices --version 0.1.0
                    
NuGet\Install-Package GunsNPrices -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GunsNPrices" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GunsNPrices" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="GunsNPrices" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GunsNPrices --version 0.1.0
                    
#r "nuget: GunsNPrices, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GunsNPrices@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GunsNPrices&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=GunsNPrices&version=0.1.0
                    
Install as a Cake Tool

GunsNPrices for .NET

NuGet CI License

Parse firearm and ammunition product titles into structured data: brand, model, caliber, magazine capacity, barrel length, rounds per box and cost per round.

using GunsNPrices;

var r = TitleParser.Parse("Glock 19 Gen 5 MOS 9mm 4.02in 15rd Pistol PA195S203MOS");
r.Model?.Model;     // "Glock 19 Gen 5 MOS"
r.Caliber?.Name;    // "9×19mm Parabellum"
r.Capacity;         // 15
r.BarrelLength;     // 4.02

This is the title parser behind GunsNPrices, a price comparison site that tracks 40+ US gun and ammo stores. Store titles are messy (G19 GEN5 9MM 15RD FS, O/U 12/28 BL/WD 3", Federal 308 150gr ... 20rds), and matching the same product across stores starts with reading them reliably. The model dictionaries and the caliber list in this package are the ones the site uses to group listings, so you get the same model names you see on gunsnprices.com.

No dependencies. Targets .NET Standard 2.0 (for .NET Framework 4.6.2+ and other older runtimes) and .NET 8 (trimmable). About 0.16 ms per title after a one-time warm-up of about 0.4 s. It is a port of the Python package, tested against the same 1,150 recorded titles as the npm, PHP, Dart and Java ports, and gives the same answers as the Python package on all 63,755 titles of the GunsNPrices catalog.

Building on Umbraco? Umbraco.Community.GunsNPrices adds Razor helpers and a property value converter on top of this package (README).

Install

dotnet add package GunsNPrices

What it recognizes

Field Example in a title Result
Firearm or ammo ... 150gr ... 20rds Kind == ProductKind.Ammo
Brand and model (50 brands) G19 Gen5 MOS, part number PA195S203 Glock 19 Gen 5 MOS
Calibers (293) .357 Mag / .38 Spl, 6.5 CM, 12ga, 300 BLK, 7MM RM .357 Magnum, .38 S&W Special, ...
Magazine capacity 15+1, 17RD, 13rd/15rd, 10-shot 15, 17, 15, 10
Barrel length, inches 4.02in, 16 1/2", 22 BBL, O/U 12/28 4.02, 16.5, 22.0, 28.0
Rounds per box 50/Box, Box of 20, 1,000 Rounds, 20/25 50, 20, 1000, 20
Cost per round price 449.99 + 1000 Rounds 0.45

Brands with model dictionaries: Barrett, Benelli, Beretta, Bergara, Browning, Cadex, Canik, Charles Daly, Chiappa, Christensen Arms, Cimarron, CMMG, Colt, CVA, CZ, Daniel Defense, Desert Eagle, Diamondback, Fierce, FN, Glock, Great Lakes, Henry, Heritage, HK, Howa, Kel-Tec, Kimber, Kriss, Marlin, Mossberg, Remington, Retay, Rossi, Ruger, Savage, SCCY, Seekins, Shadow Systems, Sig Sauer, Smith & Wesson, Springfield, Stevens, Stoeger, Taurus, Tikka, Tristar, Walther, Weatherby, Winchester.

Usage

Everything is a static method of GunsNPrices.TitleParser. Results are immutable records, safe to cache and share between threads.

Everything at once

var a = TitleParser.Parse("Federal American Eagle 5.56x45mm 55gr FMJ 1000 Rounds", price: 449.99);
a.Kind;            // ProductKind.Ammo
a.Caliber?.Name;   // "5.56×45mm NATO"
a.Rounds;          // 1000
a.CostPerRound;    // 0.45

TitleParser.Parse(title, brand: null, mpn: null, sku: null, price: null, kind: null) returns a ParseResult:

Property Type Notes
Title string the title you passed
Kind ProductKind Firearm or Ammo; pass kind to skip the guess
Brand string brand of the identified model, else the brand you passed, else ""
Model ModelMatch? firearms only, see below
Caliber Caliber? first caliber in the title
Calibers IReadOnlyList<Caliber> all calibers, in order
Capacity int? magazine capacity (for 15+1 it is 15), firearms only
BarrelLength double? inches, firearms only
Rounds int? rounds per box, ammo only
CostPerRound double? ammo with price only, rounded to 4 decimals

Passing brand (the manufacturer field from a feed) helps with titles that don't name the brand. Passing mpn lets it decode Glock part numbers when the title says nothing useful:

var m = TitleParser.IdentifyModel("GLOCK 9MM PISTOL 15RD", brand: "Glock", mpn: "UA4350201");
// m.Model == "Glock 43", m.Source == ModelSource.Mpn

Models

var m = TitleParser.IdentifyModel("Sig Sauer P365 XL 9mm 3.7\" 12+1 Optic Ready");
m?.Family;   // "Sig Sauer P365"
m?.Model;    // "Sig Sauer P365 XL"

TitleParser.ListBrands();  // ["Barrett", "Benelli", ...] (50)

A ModelMatch has Brand, Family, Model (the most specific name), Generation, Configs (such as MOS, Compact), Features (such as Threaded Barrel, FDE) and Source (Title or Mpn). Accessories, parts, airguns and airsoft are rejected, so it returns null for "Glock 19 holster" and "Umarex Glock 19 CO2 BB pistol".

Calibers

TitleParser.FindCalibers("Henry Big Boy .44 Mag/.44 Spl 20in").Select(c => c.Name);
// ".44 Magnum", ".44 S&W Special"

TitleParser.NormalizeCaliber("308 WIN (7.62X51 NATO)");
// Caliber { Id = "308win", Name = ".308 Win", Type = "rifle", Url = "https://gunsnprices.com/ammo/rifle-ammo/308-win" }

Each Caliber has a stable Id, a display Name, a Type (handgun, rifle, rimfire, shotgun, airgun, muzzleloader) and the Url of its GunsNPrices price comparison page ("" if there is none). NormalizeCaliber() is meant for caliber fields in product feeds; FindCalibers() and FindCaliber() search free text; ListCalibers() returns all 293, sorted by name.

Numbers

TitleParser.MagazineCapacity("Springfield Hellcat Pro OSP 9mm 3.7in 15rd/17rd");  // 17
TitleParser.BarrelLength("Mossberg 590 Persuader 12/20 BL/SY 3\"");                // 20 (3" is the chamber)
TitleParser.RoundsPerBox("Winchester 308 150gr PowerMax Bonded 20rds");           // 20
TitleParser.CostPerRound(31.99, 20);                                              // 1.5995
TitleParser.LooksLikeAmmo("Hornady 9mm 115gr FTX Critical Defense 25rd");         // true

Warm-up

The data is loaded and the regular expressions are compiled on first use, about 0.4 s once per process. In a web app, call TitleParser.Warmup() at startup so the first request doesn't pay for it.

Input

Titles are strings as stores publish them: HTML entities (26&quot;, 10&#x2B;1) are decoded the way Python's html.unescape does, and null reads as an empty title.

Accuracy

Measured on 50,202 firearm and 13,555 ammunition listings from 40+ stores on gunsnprices.com (October 2026), against the attributes stored on the site (all ports give the same answers on these titles, so the figures are the same):

What Found in the title Right when found
Model (32,317 listings with a known model) 99.97% 99.8% same name as the site*
Caliber, firearms 81% 97.8%
Caliber, ammo 91% 94.9%
Magazine capacity 46% 95.6%
Barrel length 58% 94.4%
Rounds per box 80% 96.6%

"Found" is limited by the titles themselves: many stores don't put the caliber or barrel length in the title at all. Most of the "wrong" cases we checked by hand are errors in the store data, not in the parser: a 6mm Creedmoor rifle labeled 6.5 Creedmoor, a "1000 Rounds" case stored as a box of 20.

* The other 0.2% are the same model with a cleaner name: the site calls 52 listings "S&W M&P Shield Plus Plus Shield", the library says "S&W M&P Shield Plus".

Data

The model dictionaries and caliber list are embedded in the assembly as JSON (about 440 KB). They are copied unchanged from the Python package by python3 tools/sync_data.py, so all ports use the same data. The same data, with listing counts and price levels, is published as open datasets:

Development

dotnet build -c Release
dotnet test -c Release                                # 1,150 recorded titles + API tests + Umbraco package tests
dotnet test tests/GunsNPrices.Tests -c Release -p:LibraryTfm=netstandard2.0   # the same tests, netstandard2.0 build
dotnet test tests/Umbraco.Community.GunsNPrices.Tests -c Release -p:UmbracoTestVersion=18.3.1   # Umbraco package on 18
dotnet pack -c Release -o artifacts

The rules are Python regular expressions, used unchanged: .NET's Regex is Unicode-aware like Python's re. The two engines still differ on rare characters (zero-width joiners, letters outside the Basic Multilingual Plane, combining accents, the dotless ı and the long ſ), so on titles containing them the result can differ from the Python package. The JavaScript package differs from Python on such titles too; real store titles don't contain them.

Limitations

  • Titles from US retailers, in English.
  • Models are covered for the 50 brands above. Other brands still get calibers and numbers.
  • Built for firearms and ammunition. Magazines, parts and accessories get calibers and numbers but no model.

License

The code is under the MIT License. The data files models.json and calibers.json (embedded resources) are under CC BY 4.0: you can use them for anything, including commercial use, if you credit GunsNPrices (gunsnprices.com) with a link.

Questions, corrections and missing models: open an issue or write to app@gunsnprices.com. Current gun and ammo prices: gunsnprices.com.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on GunsNPrices:

Package Downloads
Umbraco.Community.GunsNPrices

Read firearm and ammunition product titles in Umbraco: brand, model (e.g. "Glock 19 Gen 5 MOS"), caliber, magazine capacity, barrel length, rounds per box and cost per round. A property value converter turns the text box properties you choose into parsed results for Razor views, ModelsBuilder and the Delivery API, and an HTML helper renders a spec list. Built on the GunsNPrices title parser (https://gunsnprices.com). Umbraco 17 and 18.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 46 11/11/2026