{ "translatorID": "ce0ab080-7d72-4fa3-ab4b-4bd8950f3379", "label": "MARC21XML", "creator": "Philipp Zumstein", "target": "xml", "minVersion": "3.0", "maxVersion": "", "priority": 100, "displayOptions": { "exportNotes": false, "Zusammenfassung": true }, "inRepository": true, "translatorType": 2, "browserSupport": "g", "lastUpdated": "2019-11-05 18:45:00" } // DISCLAIMER: // There are different cataloguing rules, specification of MARC dialects, // various usage over time and places. This export translator will follow // the current MARC21 bibliographic format which is described online: // http://www.loc.gov/marc/bibliographic/ // MODS to MARC21 mapping: // http://www.loc.gov/standards/mods/v3/mods2marc-mapping.html // source for MARC21 XML examples (replace idn number): // https://portal.dnb.de/opac.htm?method=requestMarcXml&idn=999678876 // Some more useful links: // https://github.com/zotero/translators/blob/master/MARC.js // https://github.com/zotero/translators/issues/762 // https://forums.zotero.org/discussion/38956/export-of-zotero-citation-to-marc-format-for-import-into-koha-lms var recordLength; var countFields = {"controlfield" : 0, "datafield" : 0, "subfield" : 0 }; var ns = "http://www.loc.gov/MARC21/slim"; var xmlDocument; var typeMap = { //default value "a" for every written text is taken (e.g. for book, article, report, webpage,...), everything else should be declared here "artwork" : "k", "audioRecording" : "j", "computerProgram" : "m", "film" : "g", "manuscript" : "t", "map" : "e",//"f" is normally less likely "podcast" : "i", "presentation" : "a",//slides -> a, speech -> i "radioBroadcast" : "i", "tvBroadcast" : "g", "videoRecording" : "g" }; var secondTypeMap = {//based on 008/24-27 "patent" : "j", "statute" : "l", "thesis" : "m", "case" : "v" }; String.prototype.replaceAt=function(index, character) { return this.substr(0, index) + character + this.substr(index+character.length); }; function fillZerosLeft(text, size) { if (!text) text = ''; if (typeof text == 'number') text=text.toString(); missingCharacters = size-text.length; if (missingCharacters>0) { text = Array(missingCharacters+1).join('0') + text; } return text; } // @property can either a string which will be attachad as a textNode // or just the Boolean "true" which will then just create the nodes // without any content function mapProperty(parentElement, elementName, attributes, property) { if (!property) return null; //var xmlDocument = parentElement.ownerDocument, newElement = xmlDocument.createElementNS(ns, elementName); if(attributes) { for(var i in attributes) { newElement.setAttribute(i, attributes[i]); } } if (property && (typeof property == "string" || typeof property == "number") ) { newElement.appendChild(xmlDocument.createTextNode(property)); recordLength += property.toString().length; } countFields[elementName]++;//for calculating the record length parentElement.appendChild(newElement); return newElement; } function doExport() { Zotero.setCharacterSet("utf-8"); var parser = new DOMParser(); xmlDocument = parser.parseFromString('', 'application/xml'); var item, i; while ((item = Zotero.nextItem())) { Z.debug(item); var exportNotes = Zotero.getOption("exportNotes"); //these two variables are important for several places var typeOfRecord = "a"; if (typeMap[item.itemType]) typeOfRecord = typeMap[item.itemType]; var bibliographicLevel = "m";//default if (item.itemType == "bookSection" || item.itemType == "conferencePaper" || item.itemType == "dictionaryEntry" || item.itemType == "encyclopediaArticle" || item.itemType == "journalArticle" || item.itemType == "magazineArticle" || item.itemType == "newspaperArticle") { bibliographicLevel = "a"; } //initial value recordLength = 26;// 24 length of the leader + 1 record terminator + 1 field terminator after the leader and directory var recordNode = mapProperty(xmlDocument.documentElement, "record", {"type" : "Bibliographic"} , true); var currentFieldNode; //BEGIN of the export //leader will be added later, but before this node var firstChild = mapProperty(recordNode, "controlfield", {"tag" : "001"}, item.itemID ); var cleanedDateModified = item.dateModified.replace(/\D/g , '');//format must be YYYYMMDDHHMMSS mapProperty(recordNode, "controlfield", {"tag" : "005"}, cleanedDateModified + '.0' ); var dateFirst = ZU.strToDate(item.dateAdded); var dateFirstString = dateFirst.year.substr(2,2) + fillZerosLeft(dateFirst.month,2) + fillZerosLeft(dateFirst.day,2); var date; var datePublicationString; if (item.date) { date = ZU.strToDate(item.date); if (date.year) { if (date.month) { if (date.day) { datePublicationString = 'e'+ date.year + fillZerosLeft(date.month,2) + fillZerosLeft(date.day,2); } else { datePublicationString = 'e'+date.year + fillZerosLeft(date.month,2)+'uu'; } } else { datePublicationString = 's'+date.year+' '; } } else {//date without year, possible? datePublicationString = 'n '; } } else {//unknown date datePublicationString = 'n '; } //position 18-34 where | is indicating that we don't have this information var details = '|||||||||||||||||'; if (secondTypeMap[item.itemType]) details = details.replaceAt(24-18, secondTypeMap[item.itemType]); if (item.itemType == "computerProgram") details = details.replaceAt(26-18, 'b'); if (item.itemType == "conferencePaper") details = details.replaceAt(29-18, '1'); if (item.itemType == "letter") details = details.replaceAt(33-18, 'i'); mapProperty(recordNode, "controlfield", {"tag" : "008"}, dateFirstString + datePublicationString + '|||'+ details +'||||u' ); if (item.ISBN) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "020", "ind1" : " ", "ind2" : " " } , true); var cleanedISBN = item.ISBN.replace(/[a-zA-Z,\.;:\-_]/g, '');//can there be more than one isbn in the item.ISBN field? if (cleanedISBN != item.ISBN) { mapProperty(currentFieldNode, "subfield", {"code" : "a"} , cleanedISBN ); mapProperty(currentFieldNode, "subfield", {"code" : "9"} , item.ISBN ); } else { mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.ISBN ); } } if (item.ISSN && bibliographicLevel == "m") {//see also field 773 for e.g. articles currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "022", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.ISSN ); } if (item.DOI) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "024", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.DOI ); mapProperty(currentFieldNode, "subfield", {"code" : "2"} , "doi" ); } currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "040", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "c"} , 'Zotero' );//not strictly a Marc organization code, but we should mention the Zotero as the 'cataloguing tool' somewhere and here is a good place and is implicitely referred because of the 'u' in 008/37 if (item.language) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "041", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.language ); } if (item.callNumber) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "090", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.callNumber ); } //100 vs 700 and 110 vs 710 //--> practical decision: move everything to the 7xx fields //because we cannot decide about "Main Entry" fields //especially in regards with the different cataloguing rules if (item.shortTitle) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "210", "ind1" : "0", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.shortTitle ); } if (item.title) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "245", "ind1" : "1", "ind2" : "0" } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.title ); if (bibliographicLevel == "m") { mapProperty(currentFieldNode, "subfield", {"code" : "n"} , item.volume ); } var responsibleAgents = []; for (let creator of item.creators) { responsibleAgents.push([creator.firstName, creator.lastName].join(" ")); } if (responsibleAgents.length > 0) { mapProperty(currentFieldNode, "subfield", {"code" : "c"} , responsibleAgents.join(", ")); } } if (item.edition) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "250", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.edition ); } if (item.scale) {//for cartographic maps currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "255", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.scale ); } if (item.version || item.system || item.programmingLanguage || item.company) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "256", "ind1" : " ", "ind2" : " " } , true );//is there a better place for this information? var computerFileArray = []; if (item.version) computerFileArray.push(item.version); if (item.system) computerFileArray.push(item.system); if (item.programmingLanguage) computerFileArray.push(item.programmingLanguage); if (item.company) computerFileArray.push(item.company); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , computerFileArray.join(", ") ); } if (item.publisher || item.place || date) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "260", "ind1" : " ", "ind2" : " " } , true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.place ); mapProperty(currentFieldNode, "subfield", {"code" : "b"} , item.publisher ); if (date && date.year) {//date was defined in the beginning mapProperty(currentFieldNode, "subfield", {"code" : "c"} , date.year ); } } if (item.numPages || item.numberOfVolumes || item.runningTime) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "300", "ind1" : " ", "ind2" : " " } , true ); var extensionArray = []; if (item.numberOfVolumes) { if ( item.numPages.match(/[a-zA-Z]/) ) { extensionArray.push( item.numberOfVolumes ); } else { extensionArray.push( item.numberOfVolumes + " v." ); } } if (item.numPages) { if ( item.numPages.match(/[a-zA-Z]/) ) { extensionArray.push( item.numPages ); } else { extensionArray.push( item.numPages + " p." ); } } if (item.runningTime) { extensionArray.push( item.runningTime ); } mapProperty(currentFieldNode, "subfield", {"code" : "a"} , extensionArray.join(" : ") ); } if (item.medium || item.artworkSize) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "340", "ind1" : " ", "ind2" : " " }, true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.medium ); mapProperty(currentFieldNode, "subfield", {"code" : "b"} , item.artworkSize ); } if (item.seriesTitle || item.seriesNumber) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "490", "ind1" : "0", "ind2" : " " }, true ); mapProperty(currentFieldNode, "subfield", {"code" : "a"} , item.seriesTitle ); mapProperty(currentFieldNode, "subfield", {"code" : "v"} , item.seriesNumber ); } if (item.notes.length>0 && exportNotes) { var noteArray = []; currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "500", "ind1" : " ", "ind2" : " " } , true ); for (i=0; i0) { currentFieldNode = mapProperty(recordNode, "datafield", {"tag" : "573", "ind1" : " ", "ind2" : " " } , true ); for (i=0; i'+"\n"); var serializer = new XMLSerializer(); var xmlDoc = serializer.serializeToString(xmlDocument); // simple pretty print XML for MARC XML data var pretty = xmlDoc.replace(/