ELisp: Parse Org Mode

By Xah Lee. Date: . Last updated: .

This is a tutorial on parsing org mode's format into a tree structure in different format, such as JSON or Graphviz DOT lang.

org mode sample 2019-01-25 dr5xg
org mode sample 2019-01-25

Sample org file.

org syntax example

* header
#+TITLE: Tasks
* TODO [#A] hh.1 :tag1:tag2:
:PROPERTIES:
:OWNER: Dav
:ID: 123
:END:
*some* thing in water
** hh.1.1
unordered lists
- bold *love*
- italic /slanted text/
- underline _undies_
- verbatim =as is=
- code ~1+1=3~ but +wrong+
#+BEGIN_CENTER
does not compute
#+END_CENTER
*** hh.1.1.1
#+BEGIN_EXAMPLE
var x = 3
x + 4
#+END_EXAMPLE
*** hh.1.1.2
DEADLINE: <2019-01-25 Fri>
learn emacs lisp
#+BEGIN_SRC emacs-lisp
(+ 3 4)
#+END_SRC
** hh.1.2
** hh.1.3
* hh.2
** hh.2.1
another date [2019-01-25 Fri]
more date <2019-01-25 Fri 20:08>

for org syntax summary, see Emacs: Org Mode Markup Cheatsheet

Org mode API

There are 3 “API” for org mode.

The first 2, Mapping API and Property API are part of the org documentation. They are simple to understand. Mostly, they let you manipulate the org mode file programmatically, but is less helpful if you want to extract the tree structure into another format, such as JSON or graphviz DOT language.

The org-element.el is more complicated. It is a independent real parser that can capture entire org structure into a lisp data format and regenerate the whole file.

what i want to do is to write a script/command to extract org file tree structure, including those properties and tags that are attached to the nodes, and transform the tree in some way (such as adding edge between nodes), then output in a different tree structure format, such as Graphviz's DOT lang.

In this tutorial, i cover all the org API. After reading, you should be able to write elisp code to easily extract the org tree structure and manipulate it and output in a different tree format.

To use these API, you'll need to

(require 'org)
(require 'org-element)

Function to Extract Heading Info

org-heading-components

org-heading-components
(org-heading-components)
extracts the components of the current heading. Return a list, like this.

(1 1 "TODO" 65 "hh.1" ":tag1:tag2:")

on a heading like this

* TODO [#A] hh.1 :tag1:tag2:

here's the meaning of the elements:

  • 0. level. an integer
  • 1. reduced level, different if ‘org-odd-levels-only’ is set.
  • 2. TODO keyword, or nil
  • 3. priority character (a integer), or nil if none
  • 4. headline string, or the tags string if none
  • 5. tags string, or nil.

test code

(defun tt-get-heading-info ()
  "show org-heading-components result"
  (interactive)
  (let ((x (org-heading-components)))
    (with-output-to-temp-buffer "*xah temp out*"
      (print x))))

Functions to Change Heading

Each of the following works on entry at cursor position.

org-todo
(org-todo &optional arg)
Change the TODO state of the entry.
org-priority
(org-priority &optional action)
Change the priority of an entry.
org-toggle-tag
(org-toggle-tag TAG &optional ONOFF)
Toggle the tag TAG for the current line.
org-promote
(org-promote)
Promote the current heading higher up the tree.
org-demote
(org-demote)
Demote the current heading lower down the tree.

Functions to Get/Set Properties

org-buffer-property-keys
(org-buffer-property-keys &optional SPECIALS DEFAULTS COLUMNS IGNORE-MALFORMED)
Get all property keys in the current buffer.

sample output

("ID" "OWNER")

test code

(defun tt-show-all-prop-keys ()
  "show all properties in buffer 2019-01-18"
  (interactive)
  (let ((x (org-buffer-property-keys )))
    (with-output-to-temp-buffer "*xah temp out*"
      (print x))))

Each of the following works on entry at cursor position.

org-entry-properties
(org-entry-properties &optional POM WHICH)
Get all properties of the current entry.
org-entry-get
(org-entry-get POM PROPERTY &optional INHERIT LITERAL-NIL)
Get value of PROPERTY for entry or content at cursor position POM.
org-entry-delete
(org-entry-delete POM PROPERTY)
Delete the property PROPERTY from entry at cursor position POM.
org-entry-put
(org-entry-put POM PROPERTY VALUE)
Set PROPERTY to VALUE for entry at cursor position POM.
org-insert-property-drawer
(org-insert-property-drawer)
Insert a property drawer into the current entry.

that is, it inserts

:PROPERTIES:
:END:
org-entry-get-multivalued-property
(org-entry-get-multivalued-property POM PROPERTY)
Get all PROPERTY as a whitespace-separated list of values and return the values as a list of strings.
org-entry-put-multivalued-property
(org-entry-put-multivalued-property POM PROPERTY &rest VALUES)
Set multivalued PROPERTY at cursor position POM to VALUES. VALUES should be a list of strings.
org-entry-add-to-multivalued-property
(org-entry-add-to-multivalued-property POM PROPERTY VALUE)
Treat the value of the property PROPERTY as a whitespace-separated list of values and make sure that VALUE is in this list.
org-entry-remove-from-multivalued-property
(org-entry-remove-from-multivalued-property POM PROPERTY VALUE)
Treat the value of the property PROPERTY as a whitespace-separated list of values and make sure that VALUE is not in this list.
org-entry-member-in-multivalued-property
(org-entry-member-in-multivalued-property POM PROPERTY VALUE)
Treat the value of the property PROPERTY as a whitespace-separated list of values and check if VALUE is in this list.
org-property-allowed-value-functions
Hook for functions supplying allowed values for a specific property.

Map Function to Org Entries

org-map-entries
(org-map-entries FUNC &optional MATCH SCOPE &rest SKIP)
Call FUNC at each headline selected by MATCH in SCOPE.

The function will be called without arguments, with the cursor positioned at the beginning of the headline. The return values of all calls is returned as a list.

(defun tt-get-headings ()
  "2019-01-14"
  (interactive)
  (let (($headings nil))
    (org-map-entries
     (lambda ()
       (push (org-heading-components) $headings)))
    (with-output-to-temp-buffer "*xah temp out*"
      (print $headings))))

;; sample output
;; ((2 2 nil nil "tt.2.1" nil)
;;  (1 1 nil nil "tt.2" nil)
;;  (2 2 nil nil "tt.1.3" nil)
;;  (2 2 nil nil "tt.1.2" nil)
;;  (3 3 nil nil "tt.1.1.2" nil)
;;  (3 3 nil nil "tt.1.1.1" nil)
;;  (2 2 nil nil "tt.1.1" nil)
;;  (1 1 nil nil "tt.1" nil))

org-element.el Parser

The org-element.el Parser is a complete org parser.

org-element-parse-buffer

org-element-parse-buffer
(org-element-parse-buffer &optional GRANULARITY VISIBLE-ONLY)
Returns a list data that represents the whole org file.

It returns a huge list, with tons of metadata, heavily nested, in a specific format, that is basically unreadible, but contains complete info about the org file, and other command can be called to extract structure, other info, or regenerate the org file from it.

Here's a sample output from a simple org file that's only 20 lines.

(org-data
 nil
 (section
  (:begin 1 :end 83 :contents-begin 1 :contents-end 83 :post-blank 0 :post-affiliated 1 :parent #0)
  (keyword
   (:key "COLUMNS" :value "%PRIORITY %TODO %25SCHEDULED %50ITEM %25DEADLINE %TAGS %ALLTAGS %OWNER" :begin 1 :end 83 :post-blank 0 :post-affiliated 1 :parent #1)))
 (headline
  (:raw-value "header" :begin 83 :end 108 :pre-blank 0 :contents-begin 93 :contents-end 108 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 83 :title
              (#
               ("header" 0 6
                (:parent #1))) :parent #0)
  (section
   (:begin 93 :end 108 :contents-begin 93 :contents-end 108 :post-blank 0 :post-affiliated 93 :parent #1)
   (keyword
    (:key "TITLE" :value "Tasks" :begin 93 :end 108 :post-blank 0 :post-affiliated 93 :parent #2))))
 (headline
  (:raw-value "hh.1" :begin 108 :end 316 :pre-blank 0 :contents-begin 137 :contents-end 316 :level 1 :priority 65 :tags
              (#
               ("tag1" 0 4
                (keymap
                 (keymap
                  (follow-link . mouse-face)
                  (mouse-3 . org-find-file-at-mouse)
                  (mouse-2 . org-open-at-mouse)) mouse-face highlight face
                 (org-tag org-level-1) fontified t)) #
               ("tag2" 0 4
                (keymap
                 (keymap
                  (follow-link . mouse-face)
                  (mouse-3 . org-find-file-at-mouse)
                  (mouse-2 . org-open-at-mouse)) mouse-face highlight face
                 (org-tag org-level-1) fontified t))) :todo-keyword #
              ("TODO" 0 4
               (fontified t face org-todo)) :todo-type todo :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 108 :ID "123" :OWNER "Dav" :title
              (#
               ("hh.1" 0 4
                (:parent #1))) :parent #0)
  (section
   (:begin 137 :end 199 :contents-begin 137 :contents-end 199 :post-blank 0 :post-affiliated 137 :parent #1)
   (property-drawer
    (:begin 137 :end 177 :contents-begin 150 :contents-end 171 :post-blank 0 :post-affiliated 137 :parent #2)
    (node-property
     (:key "OWNER" :value "Dav" :begin 150 :end 162 :post-blank 0 :post-affiliated 150 :parent #3))
    (node-property
     (:key "ID" :value "123" :begin 162 :end 171 :post-blank 0 :post-affiliated 162 :parent #3)))
   (paragraph
    (:begin 177 :end 199 :contents-begin 177 :contents-end 199 :post-blank 0 :post-affiliated 177 :parent #2)
    (bold
     (:begin 177 :end 184 :contents-begin 178 :contents-end 182 :post-blank 1 :parent #3) #
     ("some" 0 4
      (:parent #4))) #
    ("thing in water
" 0 15
(:parent #3))))
  (headline
   (:raw-value "hh.1.1" :begin 199 :end 296 :pre-blank 0 :contents-begin 209 :contents-end 296 :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 199 :title
               (#
                ("hh.1.1" 0 6
                 (:parent #2))) :parent #1)
   (section
    (:begin 209 :end 270 :contents-begin 209 :contents-end 270 :post-blank 0 :post-affiliated 209 :parent #2)
    (plain-list
     (:type unordered :begin 209 :end 225 :contents-begin 209 :contents-end 225 :structure
            (
             (209 0 "- " nil nil nil 217)
             (217 0 "- " nil nil nil 225)) :post-blank 0 :post-affiliated 209 :parent #3)
     (item
      (:bullet "- " :begin 209 :end 217 :contents-begin 211 :contents-end 217 :checkbox nil :counter nil :structure
               (
                (209 0 "- " nil nil nil 217)
                (217 0 "- " nil nil nil 225)) :post-blank 0 :post-affiliated 209 :tag nil :parent #4)
      (paragraph
       (:begin 211 :end 217 :contents-begin 211 :contents-end 217 :post-blank 0 :post-affiliated 211 :parent #5) #
       ("list1
" 0 6
(:parent #6))))
     (item
      (:bullet "- " :begin 217 :end 225 :contents-begin 219 :contents-end 225 :checkbox nil :counter nil :structure
               (
                (209 0 "- " nil nil nil 217)
                (217 0 "- " nil nil nil 225)) :post-blank 0 :post-affiliated 217 :tag nil :parent #4)
      (paragraph
       (:begin 219 :end 225 :contents-begin 219 :contents-end 225 :post-blank 0 :post-affiliated 219 :parent #5) #
       ("list2
" 0 6
(:parent #6)))))
    (center-block
     (:begin 225 :end 270 :contents-begin 240 :contents-end 257 :post-blank 0 :post-affiliated 225 :parent #3)
     (paragraph
      (:begin 240 :end 257 :contents-begin 240 :contents-end 257 :post-blank 0 :post-affiliated 240 :parent #4) #
      ("does not compute
" 0 17
(:parent #5)))))
   (headline
    (:raw-value "hh.1.1.1" :begin 270 :end 283 :pre-blank 0 :contents-begin nil :contents-end nil :level 3 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 270 :title
                (#
                 ("hh.1.1.1" 0 8
                  (:parent #3))) :parent #2))
   (headline
    (:raw-value "hh.1.1.2" :begin 283 :end 296 :pre-blank 0 :contents-begin nil :contents-end nil :level 3 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 283 :title
                (#
                 ("hh.1.1.2" 0 8
                  (:parent #3))) :parent #2)))
  (headline
   (:raw-value "hh.1.2" :begin 296 :end 306 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 296 :title
               (#
                ("hh.1.2" 0 6
                 (:parent #2))) :parent #1))
  (headline
   (:raw-value "hh.1.3" :begin 306 :end 316 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 306 :title
               (#
                ("hh.1.3" 0 6
                 (:parent #2))) :parent #1)))
 (headline
  (:raw-value "hh.2" :begin 316 :end 333 :pre-blank 0 :contents-begin 323 :contents-end 333 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 316 :title
              (#
               ("hh.2" 0 4
                (:parent #1))) :parent #0)
  (headline
   (:raw-value "hh.2.1" :begin 323 :end 333 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 323 :title
               (#
                ("hh.2.1" 0 6
                 (:parent #2))) :parent #1))))

look at this output closely, because the rest of org-element.el functions work on this org-data format.

if we just call (org-element-parse-buffer 'headline ), here's sample output:

(org-data
 nil
 (headline
  (:raw-value "header" :begin 83 :end 108 :pre-blank 0 :contents-begin 93 :contents-end 108 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 83 :title "header" :parent #0))
 (headline
  (:raw-value "hh.1" :begin 108 :end 316 :pre-blank 0 :contents-begin 137 :contents-end 316 :level 1 :priority 65 :tags
              (#
               ("tag1" 0 4
                (keymap
                 (keymap
                  (follow-link . mouse-face)
                  (mouse-3 . org-find-file-at-mouse)
                  (mouse-2 . org-open-at-mouse)) mouse-face highlight face
                 (org-tag org-level-1) fontified t)) #
               ("tag2" 0 4
                (keymap
                 (keymap
                  (follow-link . mouse-face)
                  (mouse-3 . org-find-file-at-mouse)
                  (mouse-2 . org-open-at-mouse)) mouse-face highlight face
                 (org-tag org-level-1) fontified t))) :todo-keyword #
              ("TODO" 0 4
               (fontified t face org-todo)) :todo-type todo :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 108 :ID "123" :OWNER "Dav" :title "hh.1" :parent #0)
  (headline
   (:raw-value "hh.1.1" :begin 199 :end 296 :pre-blank 0 :contents-begin 209 :contents-end 296 :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 199 :title "hh.1.1" :parent #1)
   (headline
    (:raw-value "hh.1.1.1" :begin 270 :end 283 :pre-blank 0 :contents-begin nil :contents-end nil :level 3 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 270 :title "hh.1.1.1" :parent #2))
   (headline
    (:raw-value "hh.1.1.2" :begin 283 :end 296 :pre-blank 0 :contents-begin nil :contents-end nil :level 3 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 283 :title "hh.1.1.2" :parent #2)))
  (headline
   (:raw-value "hh.1.2" :begin 296 :end 306 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 296 :title "hh.1.2" :parent #1))
  (headline
   (:raw-value "hh.1.3" :begin 306 :end 316 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 306 :title "hh.1.3" :parent #1)))
 (headline
  (:raw-value "hh.2" :begin 316 :end 333 :pre-blank 0 :contents-begin 323 :contents-end 333 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 316 :title "hh.2" :parent #0)
  (headline
   (:raw-value "hh.2.1" :begin 323 :end 333 :pre-blank 0 :contents-begin nil :contents-end nil :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 323 :title "hh.2.1" :parent #1))))

Basically, the org-data has this form:

(org-data nil e1 e2 e3 … )

Each of the e1 etc has this elm_form:

(elm_type_name plist x)

where the x may be 0 or 1 elm_form. (possibly more, but so far i've just seen 0 or 1.)

The elm_type_name are like these:

they are lisp symbols.

The plist is a lisp property list of key value pairs. Each key's name start with a colon “:”, like this:

(:raw-value "header" :begin 83 :end 108 :pre-blank 0 :contents-begin 93 :contents-end 108 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 83 :title "header" :parent #0)

(plist here is lisp language's Property List data structure, not org mode's properties of “drawer”.) [see ELisp: Property List]

here's a example of elm_form

(section
   (:begin 137 :end 199 :contents-begin 137 :contents-end 199 :post-blank 0 :post-affiliated 137 :parent #1)
   (property-drawer
    (:begin 137 :end 177 :contents-begin 150 :contents-end 171 :post-blank 0 :post-affiliated 137 :parent #2)
    (node-property
     (:key "OWNER" :value "Dav" :begin 150 :end 162 :post-blank 0 :post-affiliated 150 :parent #3))
    (node-property
     (:key "ID" :value "123" :begin 162 :end 171 :post-blank 0 :post-affiliated 162 :parent #3)))
   (paragraph
    (:begin 177 :end 199 :contents-begin 177 :contents-end 199 :post-blank 0 :post-affiliated 177 :parent #2)
    (bold
     (:begin 177 :end 184 :contents-begin 178 :contents-end 182 :post-blank 1 :parent #3) #
     ("some" 0 4
      (:parent #4))) #
    ("thing in water
" 0 15
(:parent #3))))

here's another example of elm_form

(paragraph
    (:begin 177 :end 199 :contents-begin 177 :contents-end 199 :post-blank 0 :post-affiliated 177 :parent #2)
    (bold
     (:begin 177 :end 184 :contents-begin 178 :contents-end 182 :post-blank 1 :parent #3) #
     ("some" 0 4
      (:parent #4))) #
    ("thing in water
" 0 15
(:parent #3)))

sample test code

(defun tt-parse-buff ()
  "2019-01-14"
  (interactive)
  (let ((tt (org-element-parse-buffer )))
    (with-output-to-temp-buffer "*xah temp out*"
      (print tt))))
(defun tt-headline ()
  "2019-01-14"
  (interactive)
  (let ((tt (org-element-parse-buffer 'headline )))
    (with-output-to-temp-buffer "*xah temp out*"
      (print tt))))

Element Type Names

The possible element type name (symbols) are stored in var org-element-all-elements. Its value by default is a list of these:

There is also: org-element-all-objects. Its value is a list of these:

Plist Key Meaning

Each org syntactic unit (called greater element, element, and object) has different keys meaningful to it.

The following keys are shared by all:

:begin
Cursor position that begins it.
:end
Cursor position that ends it.
:post-blank
Number of trailing whitespace.
:parent
Parent element, or nil.

Some elements also have :contents-begin and :contents-end. Values are cursor positions.

Complete list of plist keys and their meaning for each element is at org-element official doc https://orgmode.org/worg/dev/org-element-api.html

org-element-at-point

org-element-at-point
(org-element-at-point)
Return a list data that's info about the element at cursor position.

Sample return values

(headline (:raw-value "tt.1.1" :begin 32 :end 114 :pre-blank 0 :contents-begin 42 :contents-end 114 :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 32 :title "tt.1.1"))
(paragraph (:begin 8 :end 30 :contents-begin 8 :contents-end 30 :post-blank 0 :post-affiliated 8 :parent nil))
(center-block (:begin 40 :end 86 :contents-begin 55 :contents-end 72 :post-blank 1 :post-affiliated 40 :parent nil))

test code

(defun tt-elm-at-p ()
  "2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (print (org-element-at-point))))

org-element-context

org-element-context
(org-element-context &optional ELEMENT)
Return smallest element or object around point. Return value is a list like (TYPE PROPS) where TYPE is the type of the element or object and PROPS a plist of properties associated to it.

Sample return value:

(headline (:raw-value "tt.1.1" :begin 30 :end 112 :pre-blank 0 :contents-begin 40 :contents-end 112 :level 2 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 30 :title "tt.1.1")) 

test code

(defun tt-elm-context ()
  "2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (print (org-element-context))))

org-element-type

org-element-type
(org-element-type elm_form)
Return a symbol, that's the type of elm_form. elm_form is the data returned by (org-element-at-point) and others.

Sample return values:

paragraph
headline
center-block
(defun tt-el-type-at-p ()
  "2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (print (org-element-type (org-element-at-point)))))

org-element-property

org-element-property
(org-element-property PROPERTY ELEMENT)
Extract the value from the PROPERTY of an ELEMENT.

for example, this org file, if cursor is on first line

* TODO [#A] hh.1 :tag1:tag2:
:PROPERTIES:
:OWNER: Dav
:ID: 123
:END:

and we eval (org-element-property :ID (org-element-at-point))

returns "123"

test code

(defun tt-get-property-id ()
  "2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (print (org-element-property :ID (org-element-at-point)))))

org-element-contents

org-element-contents
Returns an ordered (by buffer position) list of all elements or objects within a given element or object.

org-element-map

org-element-map
(org-element-map DATA TYPES FUN &optional INFO FIRST-MATCH NO-RECURSION WITH-AFFILIATED)

Map a function on selected elements or objects.

DATA is a parse tree, an element, an object, a string, or a list of such constructs. TYPES is a symbol or list of symbols of elements or objects types (see org-element-all-elements and org-element-all-objects for a complete list of types). FUN is the function called on the matching element or object. It has to accept one argument: the element or object itself.

Alt+x describe-function
for lots detail and example.

Sample

(defun tt-print-headings ()
  "print all headings in current buffer (of org mode).
2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (org-element-map (org-element-parse-buffer) 'headline
      (lambda (x)
        (princ (org-element-property :raw-value x))
        (terpri )))))
(defun tt-print-heading-has-id ()
  "print headings that have property named :ID: and print the property value.
2019-01-14"
  (interactive)
  (with-output-to-temp-buffer "*xah temp out*"
    (org-element-map (org-element-parse-buffer) 'headline
      (lambda (x)
        (let ((myid (org-element-property :ID x)))
          (when myid
            (princ "heading: " )
            (princ (org-element-property :raw-value x))
            (terpri )
            (princ "ID value is: " )
            (princ myid )))))))

org-data to org text

org-element-interpret-data
(org-element-interpret-data DATA)
Returns a string of org format. It's the reverse of org-element-parse-buffer

The DATA can be anything those org-element function returns.

When called on an element or object obtained through org-element-at-point or org-element-context, its contents will not appear, since this information is not available.

Get Parent of a Element

org-element-lineage
(org-element-lineage BLOB &optional TYPES WITH-SELF)
List all ancestors of a given element or object. BLOB is an object or element.

Another org mode API, supposedly better, but i haven't looked into, is https://github.com/Fuco1/orgba

Org Mode