AdifParser 1.0.0
dotnet add package AdifParser --version 1.0.0
NuGet\Install-Package AdifParser -Version 1.0.0
<PackageReference Include="AdifParser" Version="1.0.0" />
<PackageVersion Include="AdifParser" Version="1.0.0" />
<PackageReference Include="AdifParser" />
paket add AdifParser --version 1.0.0
#r "nuget: AdifParser, 1.0.0"
#:package AdifParser@1.0.0
#addin nuget:?package=AdifParser&version=1.0.0
#tool nuget:?package=AdifParser&version=1.0.0
AdifParser
A .NET library for parsing, building, and generating ADIF (Amateur Data Interchange Format) files.
Based on the original ADIFLib.
test adi is downloaded from ADIF Specifications : Released Version 3.1.7
Installation
dotnet add package AdifParser
Quick Start
Parse an ADIF file
using AdifParser;
var doc = new AdifDocument("log.adi");
Console.WriteLine($"QSO count: {doc.QsoCount}");
foreach (var qso in doc.Qsos)
{
Console.WriteLine($"{qso.GetFieldValue("CALL")} on {qso.GetFieldValue("BAND")}");
}
Parse an ADIF string
var doc = new AdifDocument();
doc.ReadFromString(@"Wavelog ADIF export
<ADIF_VER:5>3.1.7
<PROGRAMID:7>Wavelog
<PROGRAMVERSION:5>3.0.1
<EOH>
<BAND:3>10m
<CALL:6>XXXXXX
<DXCC:3>287
<GRIDSQUARE:6>XXXXXX
<ITUZ:2>28
<MODE:3>SSB
<NAME:4>Kite
<OPERATOR:6>XXXXXX
<HRDLOG_QSO_UPLOAD_STATUS:1>Y
<QRZCOM_QSO_UPLOAD_STATUS:1>Y
<QSL_RCVD:1>N
<QSL_SENT:1>Y
<QSL_SENT_VIA:1>B
<QSL_VIA:18>QSL -BUREAU, E-QSL
<RST_RCVD:2>59
<RST_SENT:2>59
<STATE:2>SG
<SUBMODE:3>USB
<TX_PWR:2>20
<FREQ:6>28.695
<QSO_DATE:8>20201213
<TIME_ON:6>102900
<QSO_DATE_OFF:8>20201213
<TIME_OFF:6>102900
<EOR>");
Console.WriteLine(doc.HasHeader); // True
Console.WriteLine(doc.QsoCount); // 1
var qso = doc.Qsos[0];
Console.WriteLine(qso.GetFieldValue("CALL")); // XXXX
Console.WriteLine(qso.GetFieldValue("BAND")); // 10m
Console.WriteLine(qso.GetFieldValue("MODE")); // SSB
Build and save
var doc = new AdifDocument();
doc.AddHeader("<PROGRAMID:9>AdifParser<EOH>");
var qso = new AdifQso();
qso.AddToken("CALL", "NV9U");
qso.AddToken("BAND", "80M");
qso.AddToken("MODE", "SSB");
doc.AddQso(qso);
doc.SaveToFile("output.adi");
Usage Guide
Working with the Header
// Parse a header from a string
var header = new AdifHeader("<PROGRAMID:9>AdifParser<PROGRAMVERSION:3>2.0<EOH>");
Console.WriteLine(header[0].Name); // PROGRAMID
Console.WriteLine(header[0].Data); // AdifParser
Console.WriteLine(header[1].Name); // PROGRAMVERSION
Console.WriteLine(header[1].Data); // 2.0
// Build a header programmatically
var header = new AdifHeader();
header.Add(new TokenField("PROGRAMID", "AdifParser"));
header.Add(new TokenField("PROGRAMVERSION", "2.0"));
Console.WriteLine(header);
// <PROGRAMID:9>AdifParser <PROGRAMVERSION:3>2.0 <eoh>
// Headers can have preamble text before the first tag
var header = new AdifHeader("MY LOG EXPORT<PROGRAMID:9>AdifParser<EOH>");
Console.WriteLine(header.Preamble); // MY LOG EXPORT
Working with QSOs
// Parse a QSO
var qso = new AdifQso("<CALL:4>NV9U<BAND:3>80M<MODE:3>SSB");
Console.WriteLine(qso[0].Name); // CALL
Console.WriteLine(qso[0].Data); // NV9U
Console.WriteLine(qso.Count); // 3
// Look up a field by name
Console.WriteLine(qso.GetFieldValue("CALL")); // NV9U
Console.WriteLine(qso.GetFieldValue("MODE")); // SSB
Console.WriteLine(qso.GetFieldValue("FREQ")); // (null)
// Build a QSO with TokenFieldList
var fields = new TokenFieldList
{
new TokenField("CALL", "NV9U"),
new TokenField("BAND", "80M"),
new TokenField("FREQ", "3.750"),
new TokenField("MODE", "SSB")
};
var qso = new AdifQso(fields);
Console.WriteLine(qso);
// <CALL:4>NV9U <BAND:3>80M <FREQ:5>3.750 <MODE:3>SSB <eor>
Working with Tokens
A Token is the fundamental building block of ADIF — a single key-value field in the form <TagName:Length>Data. For example, <CALL:4>NV9U has the tag name CALL, data length 4, and value NV9U. Every QSO and header is just a collection of tokens.
Tokens are case-insensitive by convention (ADIF does not distinguish call and CALL), but can be matched case-sensitively when needed.
// Parse an individual token
var token = new Token("<CALL:4>NV9U");
Console.WriteLine(token.Name); // CALL
Console.WriteLine(token.Data); // NV9U
Console.WriteLine(token.Length); // 4
// Build a token from parts
var token = new Token("CALL", "NV9U");
Console.WriteLine(token.ToString()); // <CALL:4>NV9U
// Length auto-updates when data changes
var token = new Token("CALL", "NV9U");
Console.WriteLine(token.Length); // 4
token.Data = "W1AW";
Console.WriteLine(token.Length); // 4
token.Data = "LONGCALL";
Console.WriteLine(token.Length); // 8
Working with USERDEF Fields
ADIF allows user-defined header fields with custom data types and enumerated values.
// Parse a USERDEF token
var token = new Token("<USERDEF1:5:S>Hello,{A,B,C}", isHeader: true);
Console.WriteLine(token.Name); // USERDEF1
Console.WriteLine(token.Data); // Hello
Console.WriteLine(token.UserDefType); // S
Console.WriteLine(token.EnumerationItems); // A,B,C
// Build a USERDEF token
var token = new Token("USERDEF1", "Hello", 'S', "A,B,C");
Console.WriteLine(token.ToString()); // <USERDEF1:13:S>Hello,{A,B,C}
// Build with UserDefField
var field = new UserDefField("USERDEF1", "Hello", 'S', "A,B,C");
Adding QSOs to a Document
var doc = new AdifDocument();
// From a raw string
doc.AddQso("<CALL:4>NV9U<BAND:3>80M<eor>");
// From an AdifQso object
var qso = new AdifQso();
qso.AddToken("CALL", "W1AW");
qso.AddToken("BAND", "20M");
doc.AddQso(qso);
Console.WriteLine(doc.QsoCount); // 2
Saving and Exporting
var doc = new AdifDocument();
doc.AddQso("<CALL:4>NV9U<BAND:3>80M");
// Save to file (throws AdifFileException if file exists)
doc.SaveToFile("export.adi");
// Save with overwrite
doc.SaveToFile("export.adi", overwrite: true);
// Get the ADIF string without saving
string result = doc.ToString();
// <CALL:4>NV9U <BAND:3>80M <eor>
Error Handling
FailFastOnParseError controls what happens after a complete <EOH> or <EOR> record is found but that record is malformed..
By default, parsing is fail-fast. Invalid tokens, malformed headers, and malformed QSO records throw AdifParseException and parsing stops at that record.
Records parsed before the error remain in the document.
// Skip malformed header/QSO records and continue with later records
var doc = new AdifDocument { FailFastOnParseError = false }; // <=== manually set to false here
doc.ReadFromString(
"<CALL:4>W1AW<eor>\n" +
"<CALL:10>BAD<eor>\n" + // invalid: declared length exceeds available data
"<CALL:4>NV9U<eor>");
Console.WriteLine(doc.QsoCount); // 2
Console.WriteLine(doc.Qsos[0].GetFieldValue("CALL")); // W1AW
Console.WriteLine(doc.Qsos[1].GetFieldValue("CALL")); // NV9U
RequireRecordTerminatorPerLine controls whether each physical input line must contain a complete record. Leave it as false to allow ADIF records to span multiple lines.
Set it to true when importing a format where each non-empty line is expected to end with <EOH> or <EOR>.
// Require each non-empty line to complete a header or QSO record
var doc = new AdifDocument { RequireRecordTerminatorPerLine = true };
try
{
doc.ReadFromString("<CALL:4>NV9U<BAND:3>80M"); // missing <eor>
}
catch (AdifParseException ex)
{
Console.WriteLine(ex.Message); // Record must end with <EOH> or <EOR> on line 0
}
Reading from a Stream
using var stream = File.OpenRead("log.adi");
var doc = new AdifDocument();
doc.ReadFromStream(stream);
Console.WriteLine(doc.QsoCount);
Also works with network streams, MemoryStream, or any Stream:
var bytes = Encoding.UTF8.GetBytes("<CALL:4>NV9U<BAND:3>80M<eor>");
using var stream = new MemoryStream(bytes);
var doc = new AdifDocument();
doc.ReadFromStream(stream);
Cancellation Support
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var doc = new AdifDocument();
doc.ReadFromString(largeAdifString, cts.Token);
Multi-Line QSOs
The parser accumulates lines until it encounters an <EOR> or <EOH> terminator, so QSO records can span multiple lines:
var doc = new AdifDocument();
doc.ReadFromString(
"<CALL:4>NV9U\n" +
"<BAND:3>80M\n" +
"<MODE:3>SSB\n" +
"<eor>");
Console.WriteLine(doc.QsoCount); // 1
Console.WriteLine(doc.Qsos[0].Count); // 3
Field data is read using the ADIF-declared length, so values may contain < and > characters without being treated as field delimiters:
var qso = new AdifQso("<COMMENT:9>A < B > C<CALL:4>NV9U");
Console.WriteLine(qso.GetFieldValue("COMMENT")); // A < B > C
Console.WriteLine(qso.GetFieldValue("CALL")); // NV9U
Building Without a Document
You can use TokenCollection directly for low-level work:
var tokens = new TokenCollection("<CALL:4>NV9U<BAND:3>80M<MODE:3>SSB");
Console.WriteLine(tokens.Count); // 3
// Add from a TokenFieldList
var fields = new TokenFieldList
{
new TokenField("CALL", "NV9U"),
new TokenField("BAND", "80M")
};
var tokens = new TokenCollection();
tokens.AddTokens(fields);
Console.WriteLine(tokens);
// <CALL:4>NV9U <BAND:3>80M
API Reference
AdifDocument
| Member | Description |
|---|---|
Header |
Gets or sets the ADIF header |
Qsos |
The collection of QSO records |
HasHeader |
Whether the document has a header |
QsoCount |
Number of QSOs in the document |
RequireRecordTerminatorPerLine |
Require each non-empty line to end with <EOH> or <EOR> |
FailFastOnParseError |
Throw on malformed header/QSO records; set to false to skip bad records and continue |
Version |
Library version string |
ReadFromFile(string) |
Parse an ADIF file |
ReadFromStream(Stream, CancellationToken) |
Parse an ADIF stream |
ReadFromString(string, CancellationToken) |
Parse an ADIF string |
SaveToFile(string, bool) |
Save to file |
AddHeader(string) / AddHeader(AdifHeader) |
Set the header |
AddQso(string) / AddQso(AdifQso) |
Add a QSO |
ToString() |
Serialize to ADIF string |
AdifHeader (extends TokenCollection)
| Member | Description |
|---|---|
Preamble |
Text before the first < tag |
Parse(string) |
Parse a raw header string |
Add(TokenField) |
Add a field to the header |
AdifQso (extends TokenCollection)
| Member | Description |
|---|---|
Parse(string) |
Parse a raw QSO string |
Constructor (TokenFieldList) |
Build from field list |
Token
| Member | Description |
|---|---|
Name |
Tag name (e.g. "CALL") |
Data |
Tag value (e.g. "NV9U") |
Length |
Data length (auto-calculated) |
IsHeader |
Whether this is a header token |
UserDefType |
USERDEF data type character |
EnumerationItems |
USERDEF enumeration values |
TokenCollection
| Member | Description |
|---|---|
ParseLine(string, bool) |
Parse a line into tokens |
AddToken(string, string, bool) |
Add a token by name and data |
AddToken(TokenField) |
Add a token from a field |
AddTokens(TokenFieldList) |
Add multiple tokens |
GetField(string) |
Find a token by tag name (case-insensitive) |
GetFieldValue(string) |
Get a token's data value by tag name |
Exception Types
| Exception | Thrown when |
|---|---|
AdifParseException |
Invalid ADIF format or structure |
AdifFileException |
File operation errors |
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. 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 was computed. 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. |
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 98 | 7/15/2026 |