Norwegian legal work routinely needs a law as readable, diffable text rather than as a web page or an object graph. Today that means opening Lovdata, copying the text, and formatting it by hand — once per law, and again whenever the law changes.
Request
Desired capability
A command that renders a Lovdata document as Markdown: a metadata header table, the chapter and paragraph structure preserved as headings, each ledd as its own paragraph, and a source attribution. Plus a form that writes the result to a file, so a whole set of laws can be regenerated in one pass.
The prior art, and why this is worth doing
Twelve Norwegian acts have already been rendered this way by hand for a real legal matter — veglova, sameigelova, servituttlova, hevdslova, jordskiftelova, burettslagslova, eierseksjonslova, friluftsloven, markaloven and others. That is this module's first concrete consumer, and it comes from actual casework rather than a hypothetical.
Those hand-made files have drifted from each other, which is the argument for the feature rather than an inconvenience beside it:
- The short title is parenthesised in one file and bracketed in another.
- One file opens with an empty
| | | header row; the others start at | Dato | ... |, so the first data row is rendered as the table header.
- The source URL appears as both
https://lovdata.no/lov/<id> and https://lovdata.no/dokument/NL/lov/<id>.
- One file carries a verification note and omits the
--- divider the others have.
Rettet appears only where it applies, and one law has an identifier with no sequence number.
Six near-identical documents produced by hand have six slightly different shapes. Generating them removes both the drift and the manual fetch-and-convert step behind it. Defining one canonical shape and normalising to it is part of the deliverable.
Acceptance criteria
- Rendering a law produces the canonical shape: metadata header table,
## per chapter, ### § N per paragraph, one paragraph per ledd, source attribution
- Paragraph identifiers with letter suffixes render correctly —
§ 1 a exists and is common
- Repealed provisions render as a repeal marker rather than being dropped or left empty
- Identifiers with no sequence number render correctly
- Metadata rows are omitted when the source document does not carry them, rather than emitted empty
- Regenerating an already hand-made law produces the canonical shape with metadata matching what Lovdata publishes
- The source link is correct and derived, not guessed
- NLOD 2.0 attribution appears in the generated output
- Works with no API key
Technical decisions
This does not need the authenticated API. The brief for this issue assumed a dependency on #6 and #7, and on the credential work in #15. It has none. The open gjeldende-lover.tar.bz2 package carries the full text and complete metadata of every current act, key-free — verified by locating all of the hand-made laws in it:
| Law |
Identifier |
Archive file |
| friluftsloven |
LOV-1957-06-28-16 |
nl-19570628-016.xml |
| veglova |
LOV-1963-06-21-23 |
nl-19630621-023.xml |
| sameigelova |
LOV-1965-06-18-6 |
nl-19650618-006.xml |
| hevdslova |
LOV-1966-12-09-1 |
nl-19661209-001.xml |
| servituttlova |
LOV-1968-11-29 |
nl-19681129-000.xml |
| burettslagslova |
LOV-2003-06-06-39 |
nl-20030606-039.xml |
| markaloven |
LOV-2009-06-05-35 |
nl-20090605-035.xml |
| jordskiftelova |
LOV-2013-06-21-100 |
nl-20130621-100.xml |
So this builds on the object model in #18 and ships in the same key-free release. servituttlova is the no-sequence-number case, and its archive file uses 000 in that position.
Metadata mapping, verified against the archive rather than assumed. Every row in the target header maps to a metadata key already inventoried in #18. Checked against veglova:
| Header row |
Metadata key |
Value in the archive |
| Dato |
legacyID |
LOV-1963-06-21-23 |
| Departement |
ministry |
Samferdselsdepartementet |
| Sist endret |
lastChangedBy |
lov/2024-12-06-74 fra 2025-01-01 |
| Ikrafttredelse |
dateInForce |
1964-01-01 |
| Rettet |
lastupdated |
2022-09-27 (rettet trykkfeil i § 54 annet ledd) |
| Korttittel |
titleShort |
Veglova – vegl |
Two of these confirm design decisions taken in #18. Rettet is lastupdated, which never appears as a bare date anywhere in the corpus — the parenthetical note is load-bearing, which is why that field keeps its raw text. And veglova labels its own fields in Nynorsk (I kraft frå, Sist endra ved), so reading the visible label rather than the class attribute would fail on exactly this law.
The source URL is derived, not chosen. dokid is NL/lov/1963-06-21-23, so the canonical link is https://lovdata.no/dokument/<dokid>. That settles the drift between the two URL forms in the hand-made files: the dokument/NL/... form is correct, and it is derivable rather than assembled by hand.
Repeal markers are real and common. 855 articles in the first 300 acts alone carry a repeal marker, as (Oppheva or (Opphevet in the paragraph heading. They must render as an explicit marker; dropping them would silently renumber a law.
Command surface. ConvertTo-LovdataMarkdown takes a document object and emits a string; Export-LovdataMarkdown writes it to a file. This follows the data-module verb vocabulary in Module types — ConvertTo-<Format> for the object-to-format direction, Export- for writing to a store.
The pivot is the neutral object, not the raw response. ConvertTo-LovdataMarkdown accepts the LovdataDocument object model from #18 rather than raw XML. That keeps one parser, means the renderer is testable without any archive, and means any future source of documents — including the authenticated endpoints in #6 — renders through the same path for free.
Test fixtures are generated, never copied. The hand-made files are a shape reference only. Fixtures are produced from the open Lovdata packages so the module's test data has clean public provenance, and the hand-made files are not treated as byte-exact goldens, since they disagree with each other. The canonical shape defined in this issue is the expected output.
Open: whether the metadata header should be a Markdown table or a YAML front-matter block. A table matches the existing files and reads well on GitHub; front matter is machine-readable and conventional for generated documents. A switch supporting both is a reasonable resolution. Decide before implementation.
Implementation plan
Canonical shape
Rendering
Tests
Documentation
Norwegian legal work routinely needs a law as readable, diffable text rather than as a web page or an object graph. Today that means opening Lovdata, copying the text, and formatting it by hand — once per law, and again whenever the law changes.
Request
Desired capability
A command that renders a Lovdata document as Markdown: a metadata header table, the chapter and paragraph structure preserved as headings, each ledd as its own paragraph, and a source attribution. Plus a form that writes the result to a file, so a whole set of laws can be regenerated in one pass.
The prior art, and why this is worth doing
Twelve Norwegian acts have already been rendered this way by hand for a real legal matter —
veglova,sameigelova,servituttlova,hevdslova,jordskiftelova,burettslagslova,eierseksjonslova,friluftsloven,markalovenand others. That is this module's first concrete consumer, and it comes from actual casework rather than a hypothetical.Those hand-made files have drifted from each other, which is the argument for the feature rather than an inconvenience beside it:
| | |header row; the others start at| Dato | ... |, so the first data row is rendered as the table header.https://lovdata.no/lov/<id>andhttps://lovdata.no/dokument/NL/lov/<id>.---divider the others have.Rettetappears only where it applies, and one law has an identifier with no sequence number.Six near-identical documents produced by hand have six slightly different shapes. Generating them removes both the drift and the manual fetch-and-convert step behind it. Defining one canonical shape and normalising to it is part of the deliverable.
Acceptance criteria
##per chapter,### § Nper paragraph, one paragraph per ledd, source attribution§ 1 aexists and is commonTechnical decisions
This does not need the authenticated API. The brief for this issue assumed a dependency on #6 and #7, and on the credential work in #15. It has none. The open
gjeldende-lover.tar.bz2package carries the full text and complete metadata of every current act, key-free — verified by locating all of the hand-made laws in it:LOV-1957-06-28-16nl-19570628-016.xmlLOV-1963-06-21-23nl-19630621-023.xmlLOV-1965-06-18-6nl-19650618-006.xmlLOV-1966-12-09-1nl-19661209-001.xmlLOV-1968-11-29nl-19681129-000.xmlLOV-2003-06-06-39nl-20030606-039.xmlLOV-2009-06-05-35nl-20090605-035.xmlLOV-2013-06-21-100nl-20130621-100.xmlSo this builds on the object model in #18 and ships in the same key-free release.
servituttlovais the no-sequence-number case, and its archive file uses000in that position.Metadata mapping, verified against the archive rather than assumed. Every row in the target header maps to a metadata key already inventoried in #18. Checked against
veglova:legacyIDLOV-1963-06-21-23ministrySamferdselsdepartementetlastChangedBylov/2024-12-06-74 fra 2025-01-01dateInForce1964-01-01lastupdated2022-09-27 (rettet trykkfeil i § 54 annet ledd)titleShortVeglova – veglTwo of these confirm design decisions taken in #18.
Rettetislastupdated, which never appears as a bare date anywhere in the corpus — the parenthetical note is load-bearing, which is why that field keeps its raw text. Andveglovalabels its own fields in Nynorsk (I kraft frå,Sist endra ved), so reading the visible label rather than theclassattribute would fail on exactly this law.The source URL is derived, not chosen.
dokidisNL/lov/1963-06-21-23, so the canonical link ishttps://lovdata.no/dokument/<dokid>. That settles the drift between the two URL forms in the hand-made files: thedokument/NL/...form is correct, and it is derivable rather than assembled by hand.Repeal markers are real and common. 855 articles in the first 300 acts alone carry a repeal marker, as
(Opphevaor(Opphevetin the paragraph heading. They must render as an explicit marker; dropping them would silently renumber a law.Command surface.
ConvertTo-LovdataMarkdowntakes a document object and emits a string;Export-LovdataMarkdownwrites it to a file. This follows the data-module verb vocabulary in Module types —ConvertTo-<Format>for the object-to-format direction,Export-for writing to a store.The pivot is the neutral object, not the raw response.
ConvertTo-LovdataMarkdownaccepts theLovdataDocumentobject model from #18 rather than raw XML. That keeps one parser, means the renderer is testable without any archive, and means any future source of documents — including the authenticated endpoints in #6 — renders through the same path for free.Test fixtures are generated, never copied. The hand-made files are a shape reference only. Fixtures are produced from the open Lovdata packages so the module's test data has clean public provenance, and the hand-made files are not treated as byte-exact goldens, since they disagree with each other. The canonical shape defined in this issue is the expected output.
Open: whether the metadata header should be a Markdown table or a YAML front-matter block. A table matches the existing files and reads well on GitHub; front matter is machine-readable and conventional for generated documents. A switch supporting both is a reasonable resolution. Decide before implementation.
Implementation plan
Canonical shape
src/functions/public/<Group>/<Group>.md, resolving each drift listed aboveRendering
ConvertTo-LovdataMarkdownaccepting aLovdataDocument##and paragraphs as### § N, preserving letter suffixesdokidand include the NLOD 2.0 attributionExport-LovdataMarkdownwriting to a file, with-ForceandSupportsShouldProcessTests
classDocumentation