rdflib 2.3.0 → 2.3.1-0007ffed
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/rdflib.min.js +1 -1
- package/dist/rdflib.min.js.map +1 -1
- package/esm/n3parser.js +45 -9
- package/esm/serialize.js +4 -2
- package/esm/serializer.js +47 -17
- package/lib/n3parser.js +45 -9
- package/lib/serialize.js +4 -2
- package/lib/serializer.d.ts +14 -8
- package/lib/serializer.js +47 -17
- package/package.json +4 -2
- package/src/n3parser.js +46 -17
- package/src/serialize.ts +4 -2
- package/src/serializer.js +52 -22
package/esm/n3parser.js
CHANGED
|
@@ -183,9 +183,19 @@ var ws = new RegExp("^[ \\t]*", 'g');
|
|
|
183
183
|
var signed_integer = new RegExp("^[-+]?[0-9]+", 'g');
|
|
184
184
|
var number_syntax = new RegExp("^([-+]?[0-9]+)(\\.[0-9]+)?([eE][-+]?[0-9]+)?", 'g');
|
|
185
185
|
var datetime_syntax = new RegExp('^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9](T[0-9][0-9]:[0-9][0-9](:[0-9][0-9](\\.[0-9]*)?)?)?Z?');
|
|
186
|
+
|
|
187
|
+
// Reused in tight loops to detect whitespace or comment after a dot
|
|
188
|
+
var wsOrHash = new RegExp("[\\s#]");
|
|
186
189
|
var digitstring = new RegExp("^[0-9]+", 'g');
|
|
187
190
|
var interesting = new RegExp("[\\\\\\r\\n\\\"]", 'g');
|
|
188
191
|
var langcode = new RegExp("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*", 'g');
|
|
192
|
+
|
|
193
|
+
// Returns true when a dot at position i should terminate a name,
|
|
194
|
+
// i.e., when the next character is whitespace, a comment start, or EOF
|
|
195
|
+
function dotTerminatesName(str, i) {
|
|
196
|
+
var next = str.charAt(i + 1);
|
|
197
|
+
return next === '' || wsOrHash.test(next);
|
|
198
|
+
}
|
|
189
199
|
function createSinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) {
|
|
190
200
|
return new SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why);
|
|
191
201
|
}
|
|
@@ -674,6 +684,16 @@ export class SinkParser {
|
|
|
674
684
|
if (j < 0) {
|
|
675
685
|
throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when ']' expected after [ <propertyList>");
|
|
676
686
|
}
|
|
687
|
+
if (str.slice(j, j + 1) == ".") {
|
|
688
|
+
// If a dot is found after a blank node, treat it as a statement terminator.
|
|
689
|
+
// Do NOT consume the '.' here: statement terminators are handled centrally by
|
|
690
|
+
// checkDot() (called by directiveOrStatement after statement()). Consuming the dot
|
|
691
|
+
// locally would bypass that unified logic and could cause inconsistencies.
|
|
692
|
+
// We do consume ']' below because it is a structural closer of the blank node,
|
|
693
|
+
// not a statement terminator.
|
|
694
|
+
res.push(subj);
|
|
695
|
+
return j; // leave '.' for checkDot()
|
|
696
|
+
}
|
|
677
697
|
if (str.slice(j, j + 1) != "]") {
|
|
678
698
|
throw BadSyntax(this._thisDoc, this.lines, str, j, "']' expected");
|
|
679
699
|
}
|
|
@@ -1122,7 +1142,17 @@ export class SinkParser {
|
|
|
1122
1142
|
return -1;
|
|
1123
1143
|
}
|
|
1124
1144
|
var i = j;
|
|
1125
|
-
while (i < pyjslib_len(str)
|
|
1145
|
+
while (i < pyjslib_len(str)) {
|
|
1146
|
+
var c = str.charAt(i);
|
|
1147
|
+
if (c === '.') {
|
|
1148
|
+
if (dotTerminatesName(str, i)) {
|
|
1149
|
+
break; // treat as statement terminator, not part of name
|
|
1150
|
+
}
|
|
1151
|
+
// else: accept '.' as part of name
|
|
1152
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1153
|
+
// Other invalid characters terminate the name
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1126
1156
|
var i = i + 1;
|
|
1127
1157
|
}
|
|
1128
1158
|
res.push(str.slice(j, i));
|
|
@@ -1148,12 +1178,15 @@ export class SinkParser {
|
|
|
1148
1178
|
var i = i + 1;
|
|
1149
1179
|
while (i < pyjslib_len(str)) {
|
|
1150
1180
|
var c = str.charAt(i);
|
|
1151
|
-
if (
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1181
|
+
if (c === '.') {
|
|
1182
|
+
if (dotTerminatesName(str, i)) {
|
|
1183
|
+
break; // dot ends the name here
|
|
1184
|
+
}
|
|
1185
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1155
1186
|
break;
|
|
1156
1187
|
}
|
|
1188
|
+
var ln = ln + c;
|
|
1189
|
+
var i = i + 1;
|
|
1157
1190
|
}
|
|
1158
1191
|
} else {
|
|
1159
1192
|
var ln = "";
|
|
@@ -1164,12 +1197,15 @@ export class SinkParser {
|
|
|
1164
1197
|
var ln = "";
|
|
1165
1198
|
while (i < pyjslib_len(str)) {
|
|
1166
1199
|
var c = str.charAt(i);
|
|
1167
|
-
if (
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1200
|
+
if (c === '.') {
|
|
1201
|
+
if (dotTerminatesName(str, i)) {
|
|
1202
|
+
break; // dot ends the name here
|
|
1203
|
+
}
|
|
1204
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1171
1205
|
break;
|
|
1172
1206
|
}
|
|
1207
|
+
var ln = ln + c;
|
|
1208
|
+
var i = i + 1;
|
|
1173
1209
|
}
|
|
1174
1210
|
res.push(new pyjslib_Tuple([pfx, ln]));
|
|
1175
1211
|
return i;
|
package/esm/serialize.js
CHANGED
|
@@ -40,7 +40,8 @@ contentType, callback, options) {
|
|
|
40
40
|
return executeCallback(null, documentString);
|
|
41
41
|
case TurtleContentType:
|
|
42
42
|
case TurtleLegacyContentType:
|
|
43
|
-
|
|
43
|
+
// Suppress = for sameAs and => for implies; preserve any user-specified flags (e.g., 'o')
|
|
44
|
+
sz.setFlags('si' + (opts.flags ? ' ' + opts.flags : ''));
|
|
44
45
|
documentString = sz.statementsToN3(newSts);
|
|
45
46
|
return executeCallback(null, documentString);
|
|
46
47
|
case NTriplesContentType:
|
|
@@ -48,7 +49,8 @@ contentType, callback, options) {
|
|
|
48
49
|
documentString = sz.statementsToNTriples(newSts);
|
|
49
50
|
return executeCallback(null, documentString);
|
|
50
51
|
case JSONLDContentType:
|
|
51
|
-
|
|
52
|
+
// turtle + dr (means no default, no relative prefix); preserve user flags
|
|
53
|
+
sz.setFlags('si dr' + (opts.flags ? ' ' + opts.flags : ''));
|
|
52
54
|
documentString = sz.statementsToJsonld(newSts); // convert via turtle
|
|
53
55
|
return executeCallback(null, documentString);
|
|
54
56
|
case NQuadsContentType:
|
package/esm/serializer.js
CHANGED
|
@@ -6,21 +6,20 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
6
6
|
** This is or was https://github.com/linkeddata/rdflib.js/blob/main/src/serializer.js
|
|
7
7
|
** Licence: MIT
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import * as ttl2jsonld from '@frogcat/ttl2jsonld';
|
|
10
|
+
import solidNs from 'solid-namespace';
|
|
11
|
+
import CanonicalDataFactory from './factories/canonical-data-factory';
|
|
11
12
|
import * as Uri from './uri';
|
|
12
13
|
import * as Util from './utils-js';
|
|
13
|
-
import CanonicalDataFactory from './factories/canonical-data-factory';
|
|
14
14
|
import { createXSD } from './xsd';
|
|
15
|
-
import solidNs from 'solid-namespace';
|
|
16
|
-
import * as ttl2jsonld from '@frogcat/ttl2jsonld';
|
|
17
15
|
export default function createSerializer(store) {
|
|
18
16
|
return new Serializer(store);
|
|
19
17
|
}
|
|
20
18
|
;
|
|
21
19
|
export class Serializer {
|
|
22
20
|
constructor(store) {
|
|
23
|
-
_defineProperty(this, "_notQNameChars", '\t\r\n !"#$%&\'()
|
|
21
|
+
_defineProperty(this, "_notQNameChars", '\t\r\n !"#$%&\'()*,+/;<=>?@[\\]^`{|}~');
|
|
22
|
+
// issue#228
|
|
24
23
|
_defineProperty(this, "_notNameChars", this._notQNameChars + ':');
|
|
25
24
|
// stringToN3: String escaping for N3
|
|
26
25
|
_defineProperty(this, "validPrefix", new RegExp(/^[a-zA-Z][a-zA-Z0-9]*$/));
|
|
@@ -53,6 +52,13 @@ export class Serializer {
|
|
|
53
52
|
this.base = base;
|
|
54
53
|
return this;
|
|
55
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Set serializer behavior flags. Letters can be combined with spaces.
|
|
58
|
+
* Examples: 'si', 'deinprstux', 'si dr', 'o'.
|
|
59
|
+
* Notable flags:
|
|
60
|
+
* - 'o': do not abbreviate to a prefixed name when the local part contains a dot
|
|
61
|
+
*/
|
|
56
62
|
setFlags(flags) {
|
|
57
63
|
this.flags = flags || '';
|
|
58
64
|
return this;
|
|
@@ -243,6 +249,28 @@ export class Serializer {
|
|
|
243
249
|
toN3(f) {
|
|
244
250
|
return this.statementsToN3(f.statements);
|
|
245
251
|
}
|
|
252
|
+
// Validate if a string is a valid PN_LOCAL per Turtle 1.1 spec
|
|
253
|
+
// Allows dots inside the local name but not as trailing character
|
|
254
|
+
// Also allows empty local names (for URIs ending in / or #)
|
|
255
|
+
isValidPNLocal(local) {
|
|
256
|
+
// Empty local name is valid (e.g., ex: for http://example.com/)
|
|
257
|
+
if (local.length === 0) return true;
|
|
258
|
+
|
|
259
|
+
// Cannot end with a dot
|
|
260
|
+
if (local[local.length - 1] === '.') return false;
|
|
261
|
+
|
|
262
|
+
// Check each character (allow dots mid-string)
|
|
263
|
+
for (var i = 0; i < local.length; i++) {
|
|
264
|
+
var ch = local[i];
|
|
265
|
+
// Dot is allowed unless it's the last character (checked above)
|
|
266
|
+
if (ch === '.') continue;
|
|
267
|
+
// Other characters must not be in the blacklist
|
|
268
|
+
if (this._notNameChars.indexOf(ch) >= 0) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
246
274
|
explicitURI(uri) {
|
|
247
275
|
if (this.flags.indexOf('r') < 0 && this.base) {
|
|
248
276
|
uri = Uri.refTo(this.base, uri);
|
|
@@ -605,13 +633,17 @@ export class Serializer {
|
|
|
605
633
|
if (j >= 0 && this.flags.indexOf('p') < 0 && (
|
|
606
634
|
// Can split at namespace but only if http[s]: URI or file: or ws[s] (why not others?)
|
|
607
635
|
uri.indexOf('http') === 0 || uri.indexOf('ws') === 0 || uri.indexOf('file') === 0)) {
|
|
608
|
-
var
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
636
|
+
var localid = uri.slice(j + 1);
|
|
637
|
+
var namesp = uri.slice(0, j + 1);
|
|
638
|
+
// Don't split if namespace is just the protocol (e.g., https://)
|
|
639
|
+
// A valid namespace should have content after the protocol
|
|
640
|
+
var minNamespaceLength = uri.indexOf('://') + 4; // e.g., "http://x" minimum
|
|
641
|
+
// Also don't split if namespace is the base directory (would serialize as relative URI)
|
|
642
|
+
var baseDir = this.base ? this.base.slice(0, Math.max(this.base.lastIndexOf('/'), this.base.lastIndexOf('#')) + 1) : null;
|
|
643
|
+
var namespaceIsBaseDir = baseDir && namesp === baseDir;
|
|
644
|
+
// If flag 'o' is present, forbid dots in local part when abbreviating
|
|
645
|
+
var forbidDotLocal = this.flags.indexOf('o') >= 0 && localid.indexOf('.') >= 0;
|
|
646
|
+
var canSplit = !namespaceIsBaseDir && !forbidDotLocal && namesp.length > minNamespaceLength && this.isValidPNLocal(localid);
|
|
615
647
|
/*
|
|
616
648
|
if (uri.slice(0, j + 1) === this.base + '#') { // base-relative
|
|
617
649
|
if (canSplit) {
|
|
@@ -622,8 +654,6 @@ export class Serializer {
|
|
|
622
654
|
}
|
|
623
655
|
*/
|
|
624
656
|
if (canSplit) {
|
|
625
|
-
var localid = uri.slice(j + 1);
|
|
626
|
-
var namesp = uri.slice(0, j + 1);
|
|
627
657
|
if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) {
|
|
628
658
|
// d -> suppress default
|
|
629
659
|
if (this.flags.indexOf('k') >= 0 && this.keyords.indexOf(localid) < 0) {
|
|
@@ -844,7 +874,7 @@ export class Serializer {
|
|
|
844
874
|
results = results.concat(['<' + t + ' rdf:resource="' + relURI(st.object) + '"/>']);
|
|
845
875
|
break;
|
|
846
876
|
case 'Literal':
|
|
847
|
-
results = results.concat(['<' + t + (st.object.
|
|
877
|
+
results = results.concat(['<' + t + (st.object.language ? ' xml:lang="' + st.object.language + '"' : st.object.datatype.equals(this.xsd.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.uri) + '"') + '>' + escapeForXML(st.object.value) + '</' + t + '>']);
|
|
848
878
|
break;
|
|
849
879
|
case 'Collection':
|
|
850
880
|
results = results.concat(['<' + t + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '</' + t + '>']);
|
|
@@ -895,7 +925,7 @@ export class Serializer {
|
|
|
895
925
|
results = results.concat(['<' + qname(st.predicate) + ' rdf:resource="' + relURI(st.object) + '"/>']);
|
|
896
926
|
break;
|
|
897
927
|
case 'Literal':
|
|
898
|
-
results = results.concat(['<' + qname(st.predicate) + (st.object.
|
|
928
|
+
results = results.concat(['<' + qname(st.predicate) + (st.object.language ? ' xml:lang="' + st.object.language + '"' : st.object.datatype.equals(this.xsd.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.value) + '"') + '>' + escapeForXML(st.object.value) + '</' + qname(st.predicate) + '>']);
|
|
899
929
|
break;
|
|
900
930
|
case 'Collection':
|
|
901
931
|
results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '</' + qname(st.predicate) + '>']);
|
package/lib/n3parser.js
CHANGED
|
@@ -191,9 +191,19 @@ var ws = new RegExp("^[ \\t]*", 'g');
|
|
|
191
191
|
var signed_integer = new RegExp("^[-+]?[0-9]+", 'g');
|
|
192
192
|
var number_syntax = new RegExp("^([-+]?[0-9]+)(\\.[0-9]+)?([eE][-+]?[0-9]+)?", 'g');
|
|
193
193
|
var datetime_syntax = new RegExp('^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9](T[0-9][0-9]:[0-9][0-9](:[0-9][0-9](\\.[0-9]*)?)?)?Z?');
|
|
194
|
+
|
|
195
|
+
// Reused in tight loops to detect whitespace or comment after a dot
|
|
196
|
+
var wsOrHash = new RegExp("[\\s#]");
|
|
194
197
|
var digitstring = new RegExp("^[0-9]+", 'g');
|
|
195
198
|
var interesting = new RegExp("[\\\\\\r\\n\\\"]", 'g');
|
|
196
199
|
var langcode = new RegExp("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*", 'g');
|
|
200
|
+
|
|
201
|
+
// Returns true when a dot at position i should terminate a name,
|
|
202
|
+
// i.e., when the next character is whitespace, a comment start, or EOF
|
|
203
|
+
function dotTerminatesName(str, i) {
|
|
204
|
+
var next = str.charAt(i + 1);
|
|
205
|
+
return next === '' || wsOrHash.test(next);
|
|
206
|
+
}
|
|
197
207
|
function createSinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why) {
|
|
198
208
|
return new SinkParser(store, openFormula, thisDoc, baseURI, genPrefix, metaURI, flags, why);
|
|
199
209
|
}
|
|
@@ -682,6 +692,16 @@ class SinkParser {
|
|
|
682
692
|
if (j < 0) {
|
|
683
693
|
throw BadSyntax(this._thisDoc, this.lines, str, i, "EOF when ']' expected after [ <propertyList>");
|
|
684
694
|
}
|
|
695
|
+
if (str.slice(j, j + 1) == ".") {
|
|
696
|
+
// If a dot is found after a blank node, treat it as a statement terminator.
|
|
697
|
+
// Do NOT consume the '.' here: statement terminators are handled centrally by
|
|
698
|
+
// checkDot() (called by directiveOrStatement after statement()). Consuming the dot
|
|
699
|
+
// locally would bypass that unified logic and could cause inconsistencies.
|
|
700
|
+
// We do consume ']' below because it is a structural closer of the blank node,
|
|
701
|
+
// not a statement terminator.
|
|
702
|
+
res.push(subj);
|
|
703
|
+
return j; // leave '.' for checkDot()
|
|
704
|
+
}
|
|
685
705
|
if (str.slice(j, j + 1) != "]") {
|
|
686
706
|
throw BadSyntax(this._thisDoc, this.lines, str, j, "']' expected");
|
|
687
707
|
}
|
|
@@ -1130,7 +1150,17 @@ class SinkParser {
|
|
|
1130
1150
|
return -1;
|
|
1131
1151
|
}
|
|
1132
1152
|
var i = j;
|
|
1133
|
-
while (i < pyjslib_len(str)
|
|
1153
|
+
while (i < pyjslib_len(str)) {
|
|
1154
|
+
var c = str.charAt(i);
|
|
1155
|
+
if (c === '.') {
|
|
1156
|
+
if (dotTerminatesName(str, i)) {
|
|
1157
|
+
break; // treat as statement terminator, not part of name
|
|
1158
|
+
}
|
|
1159
|
+
// else: accept '.' as part of name
|
|
1160
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1161
|
+
// Other invalid characters terminate the name
|
|
1162
|
+
break;
|
|
1163
|
+
}
|
|
1134
1164
|
var i = i + 1;
|
|
1135
1165
|
}
|
|
1136
1166
|
res.push(str.slice(j, i));
|
|
@@ -1156,12 +1186,15 @@ class SinkParser {
|
|
|
1156
1186
|
var i = i + 1;
|
|
1157
1187
|
while (i < pyjslib_len(str)) {
|
|
1158
1188
|
var c = str.charAt(i);
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1189
|
+
if (c === '.') {
|
|
1190
|
+
if (dotTerminatesName(str, i)) {
|
|
1191
|
+
break; // dot ends the name here
|
|
1192
|
+
}
|
|
1193
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1163
1194
|
break;
|
|
1164
1195
|
}
|
|
1196
|
+
var ln = ln + c;
|
|
1197
|
+
var i = i + 1;
|
|
1165
1198
|
}
|
|
1166
1199
|
} else {
|
|
1167
1200
|
var ln = "";
|
|
@@ -1172,12 +1205,15 @@ class SinkParser {
|
|
|
1172
1205
|
var ln = "";
|
|
1173
1206
|
while (i < pyjslib_len(str)) {
|
|
1174
1207
|
var c = str.charAt(i);
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1208
|
+
if (c === '.') {
|
|
1209
|
+
if (dotTerminatesName(str, i)) {
|
|
1210
|
+
break; // dot ends the name here
|
|
1211
|
+
}
|
|
1212
|
+
} else if (_notNameChars.indexOf(c) >= 0) {
|
|
1179
1213
|
break;
|
|
1180
1214
|
}
|
|
1215
|
+
var ln = ln + c;
|
|
1216
|
+
var i = i + 1;
|
|
1181
1217
|
}
|
|
1182
1218
|
res.push(new pyjslib_Tuple([pfx, ln]));
|
|
1183
1219
|
return i;
|
package/lib/serialize.js
CHANGED
|
@@ -47,7 +47,8 @@ contentType, callback, options) {
|
|
|
47
47
|
return executeCallback(null, documentString);
|
|
48
48
|
case _types.TurtleContentType:
|
|
49
49
|
case _types.TurtleLegacyContentType:
|
|
50
|
-
|
|
50
|
+
// Suppress = for sameAs and => for implies; preserve any user-specified flags (e.g., 'o')
|
|
51
|
+
sz.setFlags('si' + (opts.flags ? ' ' + opts.flags : ''));
|
|
51
52
|
documentString = sz.statementsToN3(newSts);
|
|
52
53
|
return executeCallback(null, documentString);
|
|
53
54
|
case _types.NTriplesContentType:
|
|
@@ -55,7 +56,8 @@ contentType, callback, options) {
|
|
|
55
56
|
documentString = sz.statementsToNTriples(newSts);
|
|
56
57
|
return executeCallback(null, documentString);
|
|
57
58
|
case _types.JSONLDContentType:
|
|
58
|
-
|
|
59
|
+
// turtle + dr (means no default, no relative prefix); preserve user flags
|
|
60
|
+
sz.setFlags('si dr' + (opts.flags ? ' ' + opts.flags : ''));
|
|
59
61
|
documentString = sz.statementsToJsonld(newSts); // convert via turtle
|
|
60
62
|
return executeCallback(null, documentString);
|
|
61
63
|
case _types.NQuadsContentType:
|
package/lib/serializer.d.ts
CHANGED
|
@@ -13,15 +13,21 @@ export class Serializer {
|
|
|
13
13
|
store: any;
|
|
14
14
|
rdfFactory: any;
|
|
15
15
|
xsd: {
|
|
16
|
-
boolean:
|
|
17
|
-
dateTime:
|
|
18
|
-
decimal:
|
|
19
|
-
double:
|
|
20
|
-
integer:
|
|
21
|
-
langString:
|
|
22
|
-
string:
|
|
16
|
+
boolean: import("./named-node").default;
|
|
17
|
+
dateTime: import("./named-node").default;
|
|
18
|
+
decimal: import("./named-node").default;
|
|
19
|
+
double: import("./named-node").default;
|
|
20
|
+
integer: import("./named-node").default;
|
|
21
|
+
langString: import("./named-node").default;
|
|
22
|
+
string: import("./named-node").default;
|
|
23
23
|
};
|
|
24
24
|
setBase(base: any): Serializer;
|
|
25
|
+
/**
|
|
26
|
+
* Set serializer behavior flags. Letters can be combined with spaces.
|
|
27
|
+
* Examples: 'si', 'deinprstux', 'si dr', 'o'.
|
|
28
|
+
* Notable flags:
|
|
29
|
+
* - 'o': do not abbreviate to a prefixed name when the local part contains a dot
|
|
30
|
+
*/
|
|
25
31
|
setFlags(flags: any): Serializer;
|
|
26
32
|
toStr(x: any): any;
|
|
27
33
|
fromStr(s: any): any;
|
|
@@ -51,6 +57,7 @@ export class Serializer {
|
|
|
51
57
|
toN3(f: any): string;
|
|
52
58
|
_notQNameChars: string;
|
|
53
59
|
_notNameChars: string;
|
|
60
|
+
isValidPNLocal(local: any): boolean;
|
|
54
61
|
explicitURI(uri: any): string;
|
|
55
62
|
statementsToNTriples(sts: any): string;
|
|
56
63
|
statementsToN3(sts: any): string;
|
|
@@ -65,4 +72,3 @@ export class Serializer {
|
|
|
65
72
|
statementsToXML(sts: any): string;
|
|
66
73
|
statementsToJsonld(sts: any): string;
|
|
67
74
|
}
|
|
68
|
-
import NamedNode from './named-node';
|
package/lib/serializer.js
CHANGED
|
@@ -7,14 +7,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.Serializer = void 0;
|
|
8
8
|
exports.default = createSerializer;
|
|
9
9
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
-
var
|
|
11
|
-
var
|
|
10
|
+
var ttl2jsonld = _interopRequireWildcard(require("@frogcat/ttl2jsonld"));
|
|
11
|
+
var _solidNamespace = _interopRequireDefault(require("solid-namespace"));
|
|
12
|
+
var _canonicalDataFactory = _interopRequireDefault(require("./factories/canonical-data-factory"));
|
|
12
13
|
var Uri = _interopRequireWildcard(require("./uri"));
|
|
13
14
|
var Util = _interopRequireWildcard(require("./utils-js"));
|
|
14
|
-
var _canonicalDataFactory = _interopRequireDefault(require("./factories/canonical-data-factory"));
|
|
15
15
|
var _xsd = require("./xsd");
|
|
16
|
-
var _solidNamespace = _interopRequireDefault(require("solid-namespace"));
|
|
17
|
-
var ttl2jsonld = _interopRequireWildcard(require("@frogcat/ttl2jsonld"));
|
|
18
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
19
17
|
/* Serialization of RDF Graphs
|
|
20
18
|
**
|
|
@@ -30,7 +28,8 @@ function createSerializer(store) {
|
|
|
30
28
|
;
|
|
31
29
|
class Serializer {
|
|
32
30
|
constructor(store) {
|
|
33
|
-
(0, _defineProperty2.default)(this, "_notQNameChars", '\t\r\n !"#$%&\'()
|
|
31
|
+
(0, _defineProperty2.default)(this, "_notQNameChars", '\t\r\n !"#$%&\'()*,+/;<=>?@[\\]^`{|}~');
|
|
32
|
+
// issue#228
|
|
34
33
|
(0, _defineProperty2.default)(this, "_notNameChars", this._notQNameChars + ':');
|
|
35
34
|
// stringToN3: String escaping for N3
|
|
36
35
|
(0, _defineProperty2.default)(this, "validPrefix", new RegExp(/^[a-zA-Z][a-zA-Z0-9]*$/));
|
|
@@ -63,6 +62,13 @@ class Serializer {
|
|
|
63
62
|
this.base = base;
|
|
64
63
|
return this;
|
|
65
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Set serializer behavior flags. Letters can be combined with spaces.
|
|
68
|
+
* Examples: 'si', 'deinprstux', 'si dr', 'o'.
|
|
69
|
+
* Notable flags:
|
|
70
|
+
* - 'o': do not abbreviate to a prefixed name when the local part contains a dot
|
|
71
|
+
*/
|
|
66
72
|
setFlags(flags) {
|
|
67
73
|
this.flags = flags || '';
|
|
68
74
|
return this;
|
|
@@ -253,6 +259,28 @@ class Serializer {
|
|
|
253
259
|
toN3(f) {
|
|
254
260
|
return this.statementsToN3(f.statements);
|
|
255
261
|
}
|
|
262
|
+
// Validate if a string is a valid PN_LOCAL per Turtle 1.1 spec
|
|
263
|
+
// Allows dots inside the local name but not as trailing character
|
|
264
|
+
// Also allows empty local names (for URIs ending in / or #)
|
|
265
|
+
isValidPNLocal(local) {
|
|
266
|
+
// Empty local name is valid (e.g., ex: for http://example.com/)
|
|
267
|
+
if (local.length === 0) return true;
|
|
268
|
+
|
|
269
|
+
// Cannot end with a dot
|
|
270
|
+
if (local[local.length - 1] === '.') return false;
|
|
271
|
+
|
|
272
|
+
// Check each character (allow dots mid-string)
|
|
273
|
+
for (var i = 0; i < local.length; i++) {
|
|
274
|
+
var ch = local[i];
|
|
275
|
+
// Dot is allowed unless it's the last character (checked above)
|
|
276
|
+
if (ch === '.') continue;
|
|
277
|
+
// Other characters must not be in the blacklist
|
|
278
|
+
if (this._notNameChars.indexOf(ch) >= 0) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
256
284
|
explicitURI(uri) {
|
|
257
285
|
if (this.flags.indexOf('r') < 0 && this.base) {
|
|
258
286
|
uri = Uri.refTo(this.base, uri);
|
|
@@ -615,13 +643,17 @@ class Serializer {
|
|
|
615
643
|
if (j >= 0 && this.flags.indexOf('p') < 0 && (
|
|
616
644
|
// Can split at namespace but only if http[s]: URI or file: or ws[s] (why not others?)
|
|
617
645
|
uri.indexOf('http') === 0 || uri.indexOf('ws') === 0 || uri.indexOf('file') === 0)) {
|
|
618
|
-
var
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
646
|
+
var localid = uri.slice(j + 1);
|
|
647
|
+
var namesp = uri.slice(0, j + 1);
|
|
648
|
+
// Don't split if namespace is just the protocol (e.g., https://)
|
|
649
|
+
// A valid namespace should have content after the protocol
|
|
650
|
+
var minNamespaceLength = uri.indexOf('://') + 4; // e.g., "http://x" minimum
|
|
651
|
+
// Also don't split if namespace is the base directory (would serialize as relative URI)
|
|
652
|
+
var baseDir = this.base ? this.base.slice(0, Math.max(this.base.lastIndexOf('/'), this.base.lastIndexOf('#')) + 1) : null;
|
|
653
|
+
var namespaceIsBaseDir = baseDir && namesp === baseDir;
|
|
654
|
+
// If flag 'o' is present, forbid dots in local part when abbreviating
|
|
655
|
+
var forbidDotLocal = this.flags.indexOf('o') >= 0 && localid.indexOf('.') >= 0;
|
|
656
|
+
var canSplit = !namespaceIsBaseDir && !forbidDotLocal && namesp.length > minNamespaceLength && this.isValidPNLocal(localid);
|
|
625
657
|
/*
|
|
626
658
|
if (uri.slice(0, j + 1) === this.base + '#') { // base-relative
|
|
627
659
|
if (canSplit) {
|
|
@@ -632,8 +664,6 @@ class Serializer {
|
|
|
632
664
|
}
|
|
633
665
|
*/
|
|
634
666
|
if (canSplit) {
|
|
635
|
-
var localid = uri.slice(j + 1);
|
|
636
|
-
var namesp = uri.slice(0, j + 1);
|
|
637
667
|
if (this.defaultNamespace && this.defaultNamespace === namesp && this.flags.indexOf('d') < 0) {
|
|
638
668
|
// d -> suppress default
|
|
639
669
|
if (this.flags.indexOf('k') >= 0 && this.keyords.indexOf(localid) < 0) {
|
|
@@ -854,7 +884,7 @@ class Serializer {
|
|
|
854
884
|
results = results.concat(['<' + t + ' rdf:resource="' + relURI(st.object) + '"/>']);
|
|
855
885
|
break;
|
|
856
886
|
case 'Literal':
|
|
857
|
-
results = results.concat(['<' + t + (st.object.
|
|
887
|
+
results = results.concat(['<' + t + (st.object.language ? ' xml:lang="' + st.object.language + '"' : st.object.datatype.equals(this.xsd.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.uri) + '"') + '>' + escapeForXML(st.object.value) + '</' + t + '>']);
|
|
858
888
|
break;
|
|
859
889
|
case 'Collection':
|
|
860
890
|
results = results.concat(['<' + t + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '</' + t + '>']);
|
|
@@ -905,7 +935,7 @@ class Serializer {
|
|
|
905
935
|
results = results.concat(['<' + qname(st.predicate) + ' rdf:resource="' + relURI(st.object) + '"/>']);
|
|
906
936
|
break;
|
|
907
937
|
case 'Literal':
|
|
908
|
-
results = results.concat(['<' + qname(st.predicate) + (st.object.
|
|
938
|
+
results = results.concat(['<' + qname(st.predicate) + (st.object.language ? ' xml:lang="' + st.object.language + '"' : st.object.datatype.equals(this.xsd.string) ? '' : ' rdf:datatype="' + escapeForXML(st.object.datatype.value) + '"') + '>' + escapeForXML(st.object.value) + '</' + qname(st.predicate) + '>']);
|
|
909
939
|
break;
|
|
910
940
|
case 'Collection':
|
|
911
941
|
results = results.concat(['<' + qname(st.predicate) + ' rdf:parseType="Collection">', collectionXMLTree(st.object, stats), '</' + qname(st.predicate) + '>']);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rdflib",
|
|
3
3
|
"description": "an RDF library for node.js. Suitable for client and server side.",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.1-0007ffed",
|
|
5
5
|
"private": false,
|
|
6
6
|
"browserslist": [
|
|
7
7
|
"> 0.5%"
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"test": "npm run test:unit && npm run test:serialize && npm run test:types",
|
|
102
102
|
"test:clean": "rimraf tests/serialize/,*",
|
|
103
103
|
"test:serialize": "npm run build && npm run test:serialize:all && npm run test:clean",
|
|
104
|
-
"test:serialize:all": "npm run test:serialize:1 && npm run test:serialize:2 && npm run test:serialize:3 && npm run test:serialize:4 && npm run test:serialize:5 && npm run test:serialize:6 && npm run test:serialize:7 && npm run test:serialize:8 && npm run test:serialize:10 && npm run test:serialize:11 && npm run test:serialize:12 && npm run test:serialize:13 && npm run test:serialize:14 && npm run test:serialize:15",
|
|
104
|
+
"test:serialize:all": "npm run test:serialize:1 && npm run test:serialize:2 && npm run test:serialize:3 && npm run test:serialize:4 && npm run test:serialize:5 && npm run test:serialize:6 && npm run test:serialize:7 && npm run test:serialize:8 && npm run test:serialize:10 && npm run test:serialize:11 && npm run test:serialize:12 && npm run test:serialize:13 && npm run test:serialize:14 && npm run test:serialize:15 && npm run test:serialize:16 && npm run test:serialize:17 && npm run test:serialize:18",
|
|
105
105
|
"test:serialize:1": "cd ./tests/serialize && node ./data.js -in=t1.ttl -format=application/rdf+xml -out=,t1.xml && fs-grep http://www.w3.org/2001/XMLSchema#integer ,t1.xml",
|
|
106
106
|
"test:serialize:2": "cd ./tests/serialize && node ./data.js -in=t2.ttl -format=application/rdf+xml -out=,t2.xml && node diff ,t2.xml t2-ref.xml",
|
|
107
107
|
"test:serialize:3": "cd ./tests/serialize && node ./data.js -in=t3.ttl -format=application/rdf+xml -out=,t3.xml && node diff ,t3.xml t3-ref.xml",
|
|
@@ -117,6 +117,8 @@
|
|
|
117
117
|
"test:serialize:14": "cd ./tests/serialize && node ./data.js -in=t14.html -format=text/turtle -out=,t14.ttl && node diff ,t14.ttl t14-ref.ttl",
|
|
118
118
|
"test:serialize:15": "cd ./tests/serialize && node ./data.js -in=t15.html -format=text/turtle -out=,t15.ttl && node diff ,t15.ttl t15-ref.ttl",
|
|
119
119
|
"test:serialize:16": "cd ./tests/serialize && node ./data.js -in=t1.ttl -format=application/ld+json -out=,t1.jsonld && node diff ,t1.jsonld t16-ref.jsonld",
|
|
120
|
+
"test:serialize:17": "cd ./tests/serialize && node ./data.js -in=t17.ttl -format=application/rdf+xml -out=,t17.xml && node diff ,t17.xml t17-ref.xml",
|
|
121
|
+
"test:serialize:18": "cd ./tests/serialize && node ./data.js -in=t18.ttl -format=application/rdf+xml -out=,t18.xml && node diff ,t18.xml t18-ref.xml",
|
|
120
122
|
"test:types": "tsc --noEmit --target es2019 --moduleResolution node tests/types/*.ts",
|
|
121
123
|
"test:unit": "mocha --growl --require ./tests/babel-register.js tests/unit/**-test.*",
|
|
122
124
|
"test:unit:egp": "mocha --require ./tests/babel-register.js tests/unit/fetcher-egp-test.js",
|