Skip to main content

Markdown

The org.apache.juneau.marshall.markdown package provides Markdown serialization and parsing for POJOs. Output is optimized for human readability and LLM/AI consumption.

Markdown support is included in juneau-marshall.

Maven
<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-marshall</artifactId>
<version>${juneau.version}</version>
</dependency>

Two Modes​

ModeSerializerUse Case
FragmentMarkdownSerializerCompact tables/lists for embedding
DocumentMarkdownDocSerializerFull documents with headings for nested structure

Fragment Mode Mapping​

Input typeMarkdown output
Single Bean2-column key-value table
Single Map2-column key-value table
Collection<Bean>Multi-column table with header row
Collection<Map>Multi-column table with header row
Collection<SimpleType>Bulleted list
Nested bean (in table cell)Inline JSON5 in backticks

Quick Start​

// A simple bean
public class Person {
public String name;
public int age;
public Person() {}
public Person(String name, int age) { this.name = name; this.age = age; }
}

// Fragment mode - table output
List<Person> people = List.of(new Person("Alice", 30), new Person("Bob", 25));
String md = MarkdownSerializer.DEFAULT.write(people);
// Produces:
// | name | age |
// |------|-----|
// | Alice | 30 |
// | Bob | 25 |

// Document mode - with headings
String doc = MarkdownDocSerializer.create().title("Report").build().write(people);

// Use the marshaller
String md2 = Markdown.of(people);
List<Person> parsed = Markdown.to(md, List.class, Person.class);

Document Mode​

Document mode uses headings for nested structure:

public class Person {
public String name;
public Address address;
}

MarkdownDocSerializer s = MarkdownDocSerializer.create().title("Person").build();
String md = s.write(person);

Produces:

# Person

| Property | Value |
|----------|-------|
| name | John |

## address

| Property | Value |
|----------|-------|
| city | Springfield |
| state | IL |

Marshallers: Markdown vs MarkdownDoc​

Each mode has its own marshaller — a serializer/parser pairing with convenience of(...) / to(...) methods:

MarshallerBacking serializer/parserOutput
MarkdownMarkdownSerializer / MarkdownParserFragment — a bare table or list, ideal for embedding inside a larger document
MarkdownDocMarkdownDocSerializer / MarkdownDocParserDocument — a full Markdown document that uses headings (#, ##, …) to represent nested structure

Use Markdown when the result will be spliced into another document (for example, a table inside an LLM prompt or a README). Use MarkdownDoc when you want a standalone document whose nesting is expressed as a heading hierarchy rather than inline JSON5.

List<Person> people = List.of(new Person("Alice", 30), new Person("Bob", 25));

// Fragment marshaller - just the table
String fragment = Markdown.DEFAULT.of(people);

// Document marshaller - full document with headings
String document = MarkdownDoc.DEFAULT.of(people);

// Both round-trip back to POJOs
List<Person> parsed = MarkdownDoc.DEFAULT.to(document, List.class, Person.class);

MarshallUtils convenience methods​

For one-off conversions without holding a marshaller reference, MarshallUtils provides static shortcuts that delegate to the Markdown.DEFAULT and MarkdownDoc.DEFAULT instances:

MethodDelegates toPurpose
markdown(Object)Markdown.DEFAULT.of(...)Serialize to a fragment
markdown(Object, Class)Markdown.DEFAULT.to(...)Parse a fragment into a POJO
markdownDoc(Object)MarkdownDoc.DEFAULT.of(...)Serialize to a whole document
markdownDoc(Object, Class)MarkdownDoc.DEFAULT.to(...)Parse a whole document into a POJO
import static org.apache.juneau.marshall.marshaller.MarshallUtils.*;

String fragment = markdown(people); // fragment mode
String document = markdownDoc(people); // document mode

Media Types​

  • Produces: text/markdown
  • Accepts: text/markdown, text/x-markdown

REST Integration​

// Markdown is included in BasicUniversalConfig
@Rest
public class MyResource extends RestServlet implements BasicUniversalConfig { ... }

Annotations​

@Markdown customizes rendering on classes, methods and fields:

AttributeDescription
formatRendering format override (MarkdownFormat).
headingCustom heading text in document mode (overrides the property name).
noTablesForce list rendering instead of tables for collections.
noHeadersSuppress the table header row.
codeRender the value as inline backtick-quoted code.
linkRender the value as a link; {value} in the URL template is replaced by the property value.
public class Person {
@Markdown(code=true) public String id;
@Markdown(link="https://example.com/users/{value}") public String login;
@Markdown(noTables=true) public List<String> tags;
}

@MarkdownConfig currently exposes only rank (serializer and parser settings are set through the builders), and @MarkdownApply applies an @Markdown to other classes/members via on()/onClass() (see Dynamically Applied Annotations).

See Also​