Meziantou.Framework.AtlassianDataFormat
1.0.1
Prefix Reserved
dotnet add package Meziantou.Framework.AtlassianDataFormat --version 1.0.1
NuGet\Install-Package Meziantou.Framework.AtlassianDataFormat -Version 1.0.1
<PackageReference Include="Meziantou.Framework.AtlassianDataFormat" Version="1.0.1" />
<PackageVersion Include="Meziantou.Framework.AtlassianDataFormat" Version="1.0.1" />
<PackageReference Include="Meziantou.Framework.AtlassianDataFormat" />
paket add Meziantou.Framework.AtlassianDataFormat --version 1.0.1
#r "nuget: Meziantou.Framework.AtlassianDataFormat, 1.0.1"
#:package Meziantou.Framework.AtlassianDataFormat@1.0.1
#addin nuget:?package=Meziantou.Framework.AtlassianDataFormat&version=1.0.1
#tool nuget:?package=Meziantou.Framework.AtlassianDataFormat&version=1.0.1
Meziantou.Framework.AtlassianDataFormat
Meziantou.Framework.AtlassianDataFormat provides:
- A typed object model for the Atlassian Document Format (ADF), the JSON rich-text format used by Jira, Confluence, and Bitbucket (
AdfDocument,AdfNode,AdfMark, ...) - A Markdown converter with per-node options for the ADF constructs Markdown cannot express (
AdfToMarkdown)
Convert a document to Markdown
using Meziantou.Framework.AtlassianDataFormat;
var json = """
{
"version": 1,
"type": "doc",
"content": [
{ "type": "heading", "attrs": { "level": 1 }, "content": [ { "type": "text", "text": "Title" } ] },
{ "type": "paragraph", "content": [ { "type": "text", "text": "Hello", "marks": [ { "type": "strong" } ] } ] }
]
}
""";
var markdown = AdfToMarkdown.Convert(json);
// # Title
//
// **Hello**
Convert also accepts a JsonElement or a JsonNode, which avoids a round trip when the document
comes from an API response you already parsed.
Configure the conversion
Defaults produce plain CommonMark/GitHub-Flavored Markdown; HTML output is opt-in.
var markdown = AdfToMarkdown.Convert(json, new AdfToMarkdownOptions
{
PanelStyle = AdfPanelStyle.GitHubAlert, // > [!WARNING]
ExpandStyle = AdfExpandStyle.HtmlDetails, // <details><summary>…</summary>
TableStyle = AdfTableStyle.Auto, // pipe table, or HTML when cells span rows/columns
MediaRendering = AdfMediaRendering.Link,
EmojiRendering = AdfEmojiRendering.ShortName,
TaskListStyle = AdfTaskListStyle.Checkbox,
DecisionListStyle = AdfDecisionListStyle.BulletList,
UnknownNodeHandling = AdfUnknownNodeHandling.KeepContent,
MentionFormat = "@{text}",
StatusFormat = "**[{text}]**",
DateFormat = "yyyy-MM-dd",
HeadingStyle = AdfHeadingStyle.Atx,
EmphasisMarker = AdfEmphasisMarker.Asterisk,
CodeBlockStyle = AdfCodeBlockStyle.Fenced,
LineBreakStyle = AdfLineBreakStyle.Backslash,
});
Resolving media and mentions
Only media of type external carries a URL. Files stored by Atlassian carry an identifier and a
collection, and turning those into a URL needs an authenticated call to the media API. Documents
returned by the APIs also often omit the display name of a mention. Both can be supplied by a
callback:
var markdown = AdfToMarkdown.Convert(json, new AdfToMarkdownOptions
{
MediaUrlResolver = media => $"https://media.example.com/{media.Collection}/{media.Id}",
MentionResolver = mention => directory.GetDisplayName(mention.Id),
});
Work with the object model
var document = AdfDocument.Parse(json);
foreach (var mention in document.Descendants().OfType<AdfMention>())
{
Console.WriteLine(mention.Id);
}
var heading = (AdfHeading)document.Content[0];
Console.WriteLine(heading.Level); // 1
var markdown = document.ToMarkdown();
var roundTripped = document.ToJsonString();
Documents can also be built from scratch:
var document = new AdfDocument
{
Content =
[
new AdfHeading { Level = 1, Content = [new AdfText { Text = "Title" }] },
new AdfParagraph { Content = [new AdfText { Text = "Hello", Marks = [new AdfStrongMark()] }] },
],
};
Console.WriteLine(document.ToJsonString());
Unknown nodes
Atlassian adds node types regularly, and real documents contain nodes such as unsupportedBlock.
Parsing never fails on an unknown type: it produces an AdfUnknownNode that keeps the original JSON,
so the document round-trips unchanged. AdfToMarkdownOptions.UnknownNodeHandling controls whether
such a node is skipped, has its content converted, or throws.
Supported nodes and marks
Nodes: blockCard, blockquote, bodiedExtension, bulletList, caption, codeBlock, date,
decisionItem, decisionList, embedCard, emoji, expand, extension, hardBreak, heading,
inlineCard, inlineExtension, layoutColumn, layoutSection, listItem, media, mediaGroup,
mediaInline, mediaSingle, mention, nestedExpand, orderedList, panel, paragraph,
placeholder, rule, status, table, tableCell, tableHeader, tableRow, taskItem,
taskList, text.
Marks: alignment, annotation, backgroundColor, border, breakout, code, em,
indentation, link, strike, strong, subsup, textColor, underline.
Marks with no Markdown equivalent — underline, textColor, backgroundColor, annotation,
border, alignment, indentation, breakout — are dropped, and the text they carry is kept.
subsup becomes <sub> or <sup>.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. net11.0 is compatible. |
-
net10.0
- No dependencies.
-
net11.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.