Usage
File and string APIs
File APIs accept a path as str or pathlib.Path. They infer the format
from a recognized suffix, unless format is supplied explicitly. Reads use
UTF-8 and missing files raise FileNotFoundError.
from pathlib import Path
from bibliography_io import load_raw, write
records = load_raw(Path("references.bib"))
write(records, path="references.ris")
String APIs require an explicit format:
from bibliography_io import to_string
from bibliography_io import loads_raw
records = loads_raw("@article{example, title={An example}}", format="bib")
bibtex = to_string(records, format="bib")
Supported formats
load_raw reads BibTeX (.bib), RIS (.ris), CSV (.csv),
Excel (.xls and .xlsx), ENL/EndNote (.enl and .txt), NBIB
(.nbib), and JSON (.json). It also accepts Markdown (.md) for
reference-list parsing only when the optional backend is installed.
write writes BibTeX, RIS, CSV, Excel (.xlsx), and Markdown
tables. JSON, ENL, NBIB, and legacy Excel (.xls) are read-only. to_string
supports BibTeX, RIS, CSV, and Markdown-table output. Excel is a file-only
format; loads_raw supports the string-based input formats.
Use write(..., primary=True) to write a primary-record mapping. This mode
validates that records have unique, non-empty IDs and valid entry types, and
only permits BibTeX output.
Serialization sorts records by their record IDs by default for both list and
mapping inputs. Set sort_by_id=False on write(), to_string(), or
write_string() to preserve list order or mapping iteration order. The
primary=True mode of write() continues to sort primary records by ID.
Format aliases include bibtex for BibTeX, excel for Excel, endnote
for ENL, and markdown for Markdown. File suffixes are case-insensitive.
Markdown forms and optional dependencies
Markdown table output is built into bibliography-io and does not need an
optional backend. Markdown reference-list parsing uses CoLRev and GROBID and is
enabled by installing bibliography-io[markdown]. A running GROBID service is
also required. Without the extra, a Markdown read raises
OptionalBackendUnavailableError. Excel loading and writing require
bibliography-io[excel]; the all extra installs both optional feature
sets.
Record shape
load_raw() and loads_raw() return records in source order as a list of
dictionaries. They preserve parser-level field names and value types: values
may be strings, lists for repeated fields, mappings, or other format-native
values. Raw loading does not require or generate record IDs, and it does not
apply source-specific normalization. Unknown fields are retained.
load() returns records in source order as a list of dictionaries with
normalized field names and string values. This applies both to generic
normalization and to results returned by source-specific normalization hooks.
Repeated values are combined into strings (authors use `` and ; keywords use
``; ``; other conflicting values use ``; ``). Empty scalar values and empty
items in repeated values are omitted. A
source-specific hook may select and normalize fields, but its non-string field
values are converted to strings before ``load() returns. Supply a source
identifier and optional source_version to use source-specific preprocessing
and normalization:
from bibliography_io import load
records = load(
"references.ris",
source="colrev.crossref",
source_version="2026",
)
Without source, load() applies bibliography-io’s generic normalization.
When a source is selected, source names the CoLRev package by its unique
endpoint identifier, such as colrev.crossref. The format is inferred from
the file suffix unless format is supplied; use format if the suffix is
ambiguous or does not match the file’s actual format. See
Source extensions for bibliography-io for package discovery and preprocessing/normalization
hook details.
DataFrame helpers
load_df() returns the normalized records from load() as DataFrame rows
in source order, with a default RangeIndex. It accepts source,
source_version, and format. load_primary_df() returns primary
records with IDs as the DataFrame index. Parser-level records remain available
as lists through load_raw() and loads_raw(). Pandas is optional; install
the Excel extra with pip install bibliography-io[excel] to use these helpers.
get_nr_records() returns the number of raw records in a file.
from bibliography_io import load_df
from bibliography_io import load_primary_df
normalized = load_df("references.ris")
primary = load_primary_df("references.bib")
Raw records have not been curated as primary records. Normalization belongs to
load() and is generic unless a source is selected. Formats retain their own
limits on what parser-level information can be represented. Unlike load(),
load_primary() and loads_primary() retain mapping and list values only
for BibTeX fields explicitly declared with mapping_fields and
list_fields; all other field values are strings. The declarations define
how those BibTeX fields are parsed, and do not change the behavior of load().
Format examples
String loaders require an explicit format. Use loads_raw() to inspect
format-specific field names and parser-level values; use loads() for
normalized field names and values. In normalized records, authors are combined
into one author string joined by the word and.
from bibliography_io import loads, loads_raw
bib = """\
@article{g2021,
title = {Trust},
author = {Guo, Wenbo and Straub, Detmar},
}
"""
loads_raw(bib, format="bib")
# [
# {
# 'ID': 'g2021',
# 'ENTRYTYPE': 'article',
# 'title': 'Trust',
# 'author': 'Guo, Wenbo and Straub, Detmar',
# }
# ]
ris = """\
TY - JOUR
TI - Trust
AU - Guo, Wenbo
AU - Straub, Detmar
ER -
"""
loads_raw(ris, format="ris")
# [
# {
# 'TY': 'JOUR',
# 'TI': 'Trust',
# 'AU': ['Guo, Wenbo', 'Straub, Detmar'],
# }
# ]
loads(ris, format="ris")
# [
# {
# 'ENTRYTYPE': 'article',
# 'title': 'Trust',
# 'author': 'Guo, Wenbo and Straub, Detmar',
# }
# ]
json_text = """[
{
"title": "Trust",
"authors": ["Guo, Wenbo", "Straub, Detmar"],
"metadata": {"rank": 1}
}
]"""
loads_raw(json_text, format="json")
# [
# {
# 'title': 'Trust',
# 'authors': ['Guo, Wenbo', 'Straub, Detmar'],
# 'metadata': {'rank': 1},
# }
# ]
loads(json_text, format="json")
# [
# {
# 'title': 'Trust',
# 'author': 'Guo, Wenbo and Straub, Detmar',
# 'metadata': "{'rank': 1}",
# 'ENTRYTYPE': 'misc',
# }
# ]
Structured BibTeX fields
Provenance mappings and lists can be written to BibTeX when callers explicitly
declare their field names. A mapping value is a Python dictionary from field
names to {"source": ..., "note": ...} dictionaries. Nested arbitrary
mappings are not supported. List values are Python lists of strings. Each
provenance or list item is serialized in the legacy semicolon-delimited form.
from bibliography_io import to_string
from bibliography_io import loads_raw
provenance = {
"author": {"source": "ebsco_bib.bib/11111111111111111", "note": ""},
"title": {"source": "manual.bib/entry1", "note": "checked"},
}
records = {
"example": {
"ENTRYTYPE": "article",
"colrev_masterdata_provenance": provenance,
}
}
text = to_string(
records,
format="bib",
mapping_fields=["colrev_masterdata_provenance"],
)
restored = loads_raw(
text,
format="bib",
mapping_fields=["colrev_masterdata_provenance"],
)
The mapping is written as entries such as author:source;;; list values are
written as semicolon-terminated items. The file APIs accept the same options.
Mapping and list values must follow these provenance conventions; values that
cannot be represented with the delimiters raise SerializationError.
Structured-field options supplied to non-BibTeX formats raise
UnsupportedFormatError. See Design principles for the full format contract.
BibTeX field ordering
BibTeX output uses a CoLRev-oriented field order by default: origin, status,
provenance, PDF and screening metadata, file and exclusion metadata, DOI and
identifiers, then bibliographic fields. This default order is
colrev_origin, colrev_status, colrev_masterdata_provenance,
colrev_data_provenance, colrev_pdf_id, screening_criteria, file,
prescreen_exclusion, doi, grobid-version, colrev.dblp.dblp_key,
colrev.semantic_scholar.id, colrev.web_of_science.unique-id,
author, booktitle, journal, title, year, volume,
number, pages, editor, publisher, url, and abstract.
Fields not in this list are written alphabetically after the CoLRev-ordered
fields. Pass field_order to to_string() or write() to choose a
different preferred order; all remaining fields are still written
alphabetically. Set sort_fields=False to preserve the input order of all
fields, including the predefined or explicitly ordered fields:
bibtex = to_string(
records,
format="bib",
field_order=["author", "title", "year", "doi"],
)
original_order = to_string(records, format="bib", sort_fields=False)
Field names in field_order are normalized like record field names. The
sort_fields option defaults to True and applies to BibTeX output;
non-BibTeX writers are unaffected.
BibTeX fields use a fixed 28-character field-name column, followed by two
spaces and =. Longer names extend beyond the aligned column. Fixed padding
keeps the whitespace on other fields unchanged as fields are added or renamed,
which helps preserve Git history diffs.