Open edsilv opened 8 years ago
fromJson
automates raw data transformation into a class definition. I added some clarifying documentation to the wiki.
I've made a test app:
https://github.com/edsilv/exjs-test
Test.ts works fine as it's just using your example. However, I'm stuck as to how I should approach Test2.ts.
https://github.com/edsilv/exjs-test/blob/master/test/Test2.ts
I need to be able to find an '@id' matching a given string.
The json I've included is a stripped-down IIIF manifest. The compiler is complaining that .en doesn't exist on an object.
Any pointers greatly appreciated...!
en only exists on arrays
Try doing [json2].en()
Cool, this works:
https://github.com/edsilv/exjs-test/blob/master/test/Test2.ts#L25
for the root-most id. What's the correct method to traverse the child nodes?
This works:
var result = [json2].en().traverseUnique(x => x.service).first(r => r['@id'] === id);
but it needs to be generic as the child arrays could be named anything.
How "generic"? How do you determine what each child array is?
See this example IIIF file: http://wellcomelibrary.org/iiif/b18035723/manifest
In the sequences array, each sequence has an @id, as does each sequence's rendering, canvas etc.
My example only uses service
as an example. I need to be able to find nodes by id for any named array at any depth. Is there a syntax to get exjs to traverse any child array regardless of name? Then my manifesto library can have a "generic" getResourceById
method.
traverse
(and traverseUnique
) relies on a flat array of objects to be returned from the functor (x => x.service
). This means that as long as you can get all objects into a flat array, traverse will walk those members.
If you have 2 members that are potential arrays, you could return them both as a single array as follows.
var result = [json2].en()
.traverseUnique(x => [].concat(x.canvases || []).concat(x.rendering || []))
.first(r => r['@id'] === id);
By extrapolating, we could write a helper function to do this for us...
var result = [json2].en()
.traverseUnique(x => getAllArrays(x))
.first(r => r['@id'] === id);
function getAllArrays(obj: any): exjs.IEnumerable<any> {
if (!obj)
return [].en();
var all = [].en();
for (var key in obj) {
var val = obj[key];
if (Array.isArray(val))
all = all.concat(val)
}
// flatten all objects into a single array
return all.selectMany(x => x);
}
I've added that to the test with this modification:
https://github.com/edsilv/exjs-test/blob/master/test/Test2.ts#L39
If I set a breakpoint there, all.selectMany(function (x) { return x; }).toArray()
yields []
.
all.toArray()
yields an array containing the found object.
Is it ok to exclude the selectMany?
Yes, I wrote the selectMany
before re-writing the inner for loop. Originally, the purpose of selectMany
was to convert a structure like this [ [...n], [...m], [...s] ]
to [...n, ...m, ...s]
.
Removing selectMany
is what you will want.
I have a new requirement for the 'manifesto' library:
https://github.com/UniversalViewer/universalviewer/issues/186
I need to be able to traverse an IIIF manifest (using traverseUnique?) to find a node with a given '@id' property. Example manifest:
http://wellcomelibrary.org/iiif/b18035723/manifest
It looks like exjs can do this:
https://github.com/BSick7/exjs/wiki/fromJson
But am I going to need to create mappings for every node type in the IIIF spec? Or can it be generalised somehow? It would be great if it just needed to know that there can be objects with an @id, or there can be arrays of objects with an @id...