pg-rdf-to-json
Version:
Transforms RDF files from rdf-files.tar.bz2 provided by Project Gutenberg into JS/JSON objects.
224 lines • 12.1 kB
JavaScript
import { relators } from '../input-schemas/relators.js';
import formatAgentId from './formatAgentId.js';
import formatEbookId from './formatEbookId.js';
import formatDatetime from './formatDatetime.js';
import formatDate from './formatDate.js';
import formatDescription from './formatDescription.js';
import formatInteger from './formatInteger.js';
import formatLanguage from './formatLanguage.js';
import formatStandaloneText from './formatStandaloneText.js';
import formatStandaloneResource from './formatStandaloneResource.js';
import formatValue from './formatValue.js';
import hoistStandaloneObjectValue from './hoistStandaloneObjectValue.js';
import formatAgent, { postProcessRelators } from './formatAgent.js';
import mergeMarcField, { postProcessMarcFields } from './mergeMarcField.js';
import { marcRanges } from '../input-schemas/marc.js';
import decodeStandaloneText from './decodeStandaloneText.js';
export const formatters = new Map([
[
'rdf.ebook.about',
formatEbookId
],
[
'rdf.ebook.files',
hoistStandaloneObjectValue
],
[
'rdf.ebook.files.file.isFormatOf.resource',
formatEbookId
],
[
'rdf.ebook.files.file.format',
formatDescription
],
[
'rdf.ebook.files.file.extent',
formatInteger
],
[
'rdf.ebook.files.file.modified',
formatDatetime
],
[
'rdf.ebook.bookshelf',
formatDescription
],
[
'rdf.ebook.subject',
formatDescription
],
[
'rdf.ebook.type',
formatDescription
],
[
'rdf.ebook.language',
formatLanguage
],
[
'rdf.ebook.alternative',
formatStandaloneText
],
[
'rdf.ebook.downloads',
formatInteger
],
[
'rdf.ebook.rights',
formatStandaloneText
],
[
'rdf.ebook.license',
formatStandaloneResource
],
[
'rdf.ebook.title',
decodeStandaloneText
],
[
'rdf.ebook.description',
formatStandaloneText
],
[
'rdf.ebook.tableOfContents',
formatStandaloneText
],
[
'rdf.ebook.publisher',
formatStandaloneText
],
[
'rdf.ebook.issued',
formatDate
],
...agentRules('rdf.ebook.creator'),
]);
/**
* The rules necessary to produce a valid `TaggedAgent` at the passed `path`.
* @param path the pass to the single agnet or array of agents.
*/
function agentRules(path) {
return [
[
path,
formatAgent,
],
[
`${path}.agent.about`,
formatAgentId
],
[
`${path}.resource`,
formatAgentId
],
[
`${path}.agent.name`,
formatStandaloneText
],
[
`${path}.agent.birthdate`,
formatInteger
],
[
`${path}.agent.deathdate`,
formatInteger
],
[
`${path}.agent.alias`,
formatStandaloneText
],
[
`${path}.agent.webpage`,
formatStandaloneResource
]
];
}
for (const [relator] of relators) {
for (const [path, formatter] of agentRules(`rdf.ebook.${relator}`)) {
formatters.set(path, formatter);
}
}
for (const [start, end] of marcRanges) {
for (let i = start; i <= end; i++) {
formatters.set(`rdf.ebook.marc${i.toString().padStart(3, '0')}`, [formatStandaloneText, mergeMarcField]);
}
}
function postProccessObject(newObject, original) {
// This fixes the extremely rare case of a book having multiple titles,
// without those titles using the alternative element like they should.
if (Array.isArray(newObject.rdf.ebook.title)) {
const moreAlternatives = newObject.rdf.ebook.title.slice(1);
newObject.rdf.ebook.title = newObject.rdf.ebook.title[0];
if (Array.isArray(newObject.rdf.ebook.alternative)) {
newObject.rdf.ebook.alternative =
moreAlternatives.concat(newObject.rdf.ebook.alternative);
}
else {
newObject.rdf.ebook.alternative = moreAlternatives;
}
}
// `subject` is a common enough field that I'd rather it always be set and
// empty, than the field itself being conditional.
if (!Object.hasOwn(newObject.rdf.ebook, 'subject')) {
newObject.rdf.ebook.subject = [];
}
// `bookshelf` is a common enough field that I'd rather it always be set and
// empty, than the field itself being conditional.
if (!Object.hasOwn(newObject.rdf.ebook, 'bookshelf')) {
newObject.rdf.ebook.bookshelf = [];
}
// `files` is extremely commonly set, so just guarantee it.
if (!Object.hasOwn(newObject.rdf.ebook, 'files')) {
newObject.rdf.ebook.files = [];
}
// Store all found relators on against a single array.
postProcessRelators(newObject, original);
// Store all found marc fields against a single object.
postProcessMarcFields(newObject, original);
return newObject;
}
/**
* Accepts a parsed, but not formatted RDF file. It isn't possible _just_ using
* `fast-xml-parser` to move keys and values around. This additional formatting
* step reduces the complexity of the parsed XML object by:
*
* * reducing the depth of unnecessesarily nested objects.
* * converting certain values into more suitable data types.
* * grouping similar fields like MARC fields.
* * enforcing structural rules e.g. `files` **must** be defined.
*
* This function recursively goes through the `original` object, returning a
* deep clone of it, but with every single one of its values parsed where a
* parser for the path has been defined.
*
* If the `original` object looks like this:
*
* ```json
* {
* "title": "hello",
* "desciption": {
* "short": "a short description",
* "long": "a long description"
* }
* }
* ```
*
* And a formatter has been defined that applies to the path
* `description.short`, but no others have been defined, then only that single
* value will be parsed. Every other value will be added to the new object
* without any changes.
*
* The formatters applied to the `original` object is defined at
* {@link formatters}.
*
* @param original The parsed, but unformatted RDF file.
* @returns A formatted RDF file.
*/
export function formatRDFFile(original) {
const newObject = {};
for (const [key, val] of Object.entries(original)) {
newObject[key] = formatValue(key, val, key, original);
}
return postProccessObject(newObject, original);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybWF0dGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9mb3JtYXR0ZXJzL2Zvcm1hdHRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBTXhELE9BQU8sYUFBYSxNQUFNLG9CQUFvQixDQUFDO0FBQy9DLE9BQU8sYUFBYSxNQUFNLG9CQUFvQixDQUFDO0FBQy9DLE9BQU8sY0FBYyxNQUFNLHFCQUFxQixDQUFDO0FBQ2pELE9BQU8sVUFBVSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pDLE9BQU8saUJBQWlCLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxhQUFhLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxjQUFjLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxvQkFBb0IsTUFBTSwyQkFBMkIsQ0FBQztBQUM3RCxPQUFPLHdCQUF3QixNQUFNLCtCQUErQixDQUFDO0FBQ3JFLE9BQU8sV0FBVyxNQUFNLGtCQUFrQixDQUFDO0FBQzNDLE9BQU8sMEJBQTBCLE1BQU0saUNBQWlDLENBQUM7QUFDekUsT0FBTyxXQUFXLEVBQUUsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3BFLE9BQU8sY0FBYyxFQUFFLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxvQkFBb0IsTUFBTSwyQkFBMkIsQ0FBQztBQUk3RCxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQTBCLElBQUksR0FBRyxDQUFDO0lBQ3ZEO1FBQ0UsaUJBQWlCO1FBQ2pCLGFBQWE7S0FDZDtJQUNEO1FBQ0UsaUJBQWlCO1FBQ2pCLDBCQUEwQjtLQUMzQjtJQUNEO1FBQ0UsMENBQTBDO1FBQzFDLGFBQWE7S0FDZDtJQUNEO1FBQ0UsNkJBQTZCO1FBQzdCLGlC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