MarkX is a useful little tool built around a useful little trick.
Markdown is text that turns into nicely balanced and escaped HTML.
We can put that HTML into any <tag> and treat it as <xml>.
This lets us query and manipulate markdown, giving us the best of both worlds.
It's easy to write text, and an easy to read document tree.
You can install MarkX from the PowerShell Gallery, using Install-Module:
Install-Module MarkXOnce install, you can import it with Import-Module
Import-Module MarkXLet's start simple, by making some markdown:
"# Hello World" | MarkXWhen we run this, we should see two properties: Markdown and XML.
Whenever we change the Markdown, everything else is updated.
Let's see how this can be useful, by getting some links.
$markXLinks =
"* [My Website](https://MrPowerShell.com/)",
"* [GitHub](https://github.com/StartAutomating)",
"* [BlueSky](https://bsky.app/profile/mrpowershell.com)" |
markx
@($markXLinks.Links.Href |
ForEach-Object {
"* $($_)"
}) -join [Environment]::NewLine
When we run this, we get:
- https://MrPowerShell.com/
- https://github.com/StartAutomating
- https://bsky.app/profile/mrpowershell.com
Let's see how this can be fun, by getting some images.
$markXImages =
"* ",
"* "
| markx
@(
$markXImages
$markXImages.Images |
ForEach-Object {
"* [$($_.alt)]($($_.src))"
}
) -join [Environment]::NewLine
When we run this, we get:
Let's make some tables
$timesTable = @(
"#### TimesTable"
foreach ($rowN in 1..9) {
$row = [Ordered]@{}
foreach ($colN in 1..9) {
$row["$colN"] = $colN * $rowN
}
$row
}
) | MarkX
$timesTable
When we run this, we get:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 |
| 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 |
| 4 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 |
| 5 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 |
| 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 54 |
| 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 |
| 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 |
| 9 | 18 | 27 | 36 | 45 | 54 | 63 | 72 | 81 |
Ok, this gets really cool.
Tables become data!
Because we can easily extract out the <table> elements inside of markdown, we can turn it into data.
And because the .NET framework includes a nifty in-memory database, we can turn this into something we can query.
A blockquote is the description
| a | b | c |
|---|---|---|
| 1 | 2 | 3 |
$markxTables = @'
#### abc
|a|b|c|
|-|-|-|
|1|2|3|
|4|5|6|
|7|8|9|
#### def
|d|e|f|
|-|-|-|
|1|2|3|
|2|4|6|
|3|6|9|
|4|8|12|
'@ | MarkX
$markxTables
$markxTables.DB.Tables["abc"].Compute('sum(a)','') # | Should -Be 12
$markxTables.DB.Tables["def"].Compute('sum(d)','') # | Should -Be 10
When we run this example, we get:
| a | b | c |
|---|---|---|
| 1 | 2 | 3 |
| 4 | 5 | 6 |
| 7 | 8 | 9 |
| d | e | f |
|---|---|---|
| 1 | 2 | 3 |
| 2 | 4 | 6 |
| 3 | 6 | 9 |
| 4 | 8 | 12 |
Since we can extra tables and data Markdown, we can also get any data of a particular known shape.
The first special shape MarkX supports is an at protocol lexicon
MarkX current supports lexicon type definitions. It will support query and procedure definitions in the future.
A type definition consists of a namespace identifier, a description, and a series of properties.
An example lexicon to record birthday messages
| Property | Type | Description |
|---|---|---|
$type |
[string] |
The type of the object. Must be com.example.happy.birthday |
message |
[string] |
A birthday message |
forUri |
[uri] |
A link |
birthday |
[datetime] |
The birthday |
createdAt |
[datetime] |
The time the record was created |
To extract out a lexicon from the text above, we can:
$lexiconMarkdown.Lexicon | ConvertTo-Json -Depth 5Which gives us:
{
"lexicon": 1,
"id": "com.example.happy.birthday",
"defs": {
"main": {
"type": "record",
"description": "com.example.happy.birthday",
"required": [
"message"
],
"properties": {
"message": {
"type": "string",
"description": "A birthday message"
},
"forUri": {
"type": "uri",
"description": "A link"
},
"birthday": {
"type": "datetime",
"description": "The birthday"
},
"createdAt": {
"type": "datetime",
"description": "The time the record was created"
}
}
}
}
}As you can see, we can take rich data within Markdown and process it into lexicons (or anything else we might want)
This allows us to query, extract, and customize markdown.
'Hello World' In Markdown / MarkX
'# Hello World' | MarkXMarkX is aliased to Markdown 'Hello World' as Markdown as XML
'# Hello World' | Markdown | Select -Expand XMLWe can generate tables by piping in objects
@{n1=1;n2=2}, @{n1=2;n3=3} | MarkXMake a TimesTable in MarkX
@(
"#### TimesTable"
foreach ($rowN in 1..9) {
$row = [Ordered]@{}
foreach ($colN in 1..9) {
$row["$colN"] = $colN * $rowN
}
$row
}
) | Get-MarkXWe can pipe a command into MarkX This will get the command help as Markdown
Get-Command Get-MarkX | MarkXWe can pipe help into MarkX
Get-Help Get-MarkX | MarkXWe can get code from markdown
Get-Help Get-MarkX |
MarkX |
Select-Object -ExpandProperty CodeMarkX is a simple and powerful tool. It allows us to turn many objects into Markdown, and turn Markdown into many objects.
Please pay around and see what you can do.

