Handling Map Parameters Passed to Custom Extension Instructions: Questions re Types
Added by Anthony Bufort 5 months ago
Hello All,
Whilst writing my custom extension function (Java-based), I expected to follow the following model for my call method
public class MyExtensionFunction extends ExtensionFunctionDefinition {
@Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
// Retrieve the map parameter passed from XSLT
XdmMap map = (XdmMap) arguments[0].head().getItem();
// Process the map parameter
for (String key : map.keySet()) {
Sequence value = map.get(key);
// Process key-value pairs as needed
}
// Your logic here
return null; // Return the result of your extension function
}
@Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[]{SequenceType.makeSequenceType(MapType.ANY_MAP_TYPE, StaticProperty.ALLOWS_ZERO_OR_MORE)};
}
@Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.SINGLE_ATOMIC;
}
}
...which is largely correct, except for the type of the map parameter coming in. It is not an XdmMap, but rather a HashTrieMap.
I have adapted, and both the function and the extension instruction variants now work using HashTrieMap. However, I'd like to gain a deeper understanding of what's going on.
Is there something in particular that I am doing (other than the simple fact that I am passing a map) to provoke that type? Is this just the way it always is - maps come in as HashTrieMaps - or can I change the type to come in as XdmMap somehow? I have so far found no way to convert, either by casting or by any provided utility method, a HashTrieMap to an XdmMap. Not that I NEED to do that at this point, but I want to know these type and any best-practices in-depth beyond what I have learned so far by looking at documentation.
I suspect there might be some clever way to easily convert between map types that I'm missing here... or perhaps not.
Thanks in Advance,
-Tony Bufort
Replies (4)
Please register to reply
RE: Handling Map Parameters Passed to Custom Extension Instructions: Questions re Types - Added by Michael Kay 5 months ago
Saxon has two layers of API, which we might call the system programming interface (SPI) and the application programming interface (API - specifically, for Java, s9api). When you write deeply embedded code like integrated extension functions, you are using the SPI.
At the s9api level, XDM values are represented by classes such as XdmValue
, XdmNode
, XdmItem
, XdmAtomicValue
, XdmMap
. These are in fact wrappers around the SPI representations, where you find classes such as Sequence
, Item
, NodeInfo
, NumericValue
, StringValue
, BooleanValue
.
At the SPI level an XDM map is represented by an object of class MapItem
. There are a number of implementations of MapItem
, one of which is HashTrieMap
. But you shouldn't assume that this will always be the implementation; you should only assume MapItem
.
RE: Handling Map Parameters Passed to Custom Extension Instructions: Questions re Types - Added by Anthony Bufort 5 months ago
Okay, thank you, Michael.
I have reworked what I'm doing, and now use MapItem. Using HashTrieMap did not seem right to me, and your explanation was just what I needed.
public static PutObjectResult uploadToS3Bucket(String bucketName, String keyName, String filePath,
MapItem metadataMap) throws XPathException {
if (! getIsInited()) {
init();
}
// disable md5 check first
System.setProperty(SkipMd5CheckStrategy.DISABLE_PUT_OBJECT_MD5_VALIDATION_PROPERTY, "true");
PutObjectRequest putRequest = new PutObjectRequest(bucketName, keyName, new File(filePath));
ObjectMetadata metadata = new ObjectMetadata();
if (metadataMap != null) {
AtomicIterator keys = metadataMap.keys();
for (AtomicValue key : keys.next()) {
metadata.addUserMetadata(key.getStringValue(), metadataMap.get(key).getStringValue());
}
putRequest.withMetadata(metadata);
}
PutObjectResult result = s3Client.putObject(putRequest);
return result;
}
-Tony
RE: Handling Map Parameters Passed to Custom Extension Instructions: Questions re Types - Added by Michael Kay 5 months ago
The for (AtomicValue key : keys.next()) {
looks wrong...
And you could perhaps shave off a couple of microseconds by doing
for (KeyValuePair kvp : metadataMap.keyValuePairs()) {
metadata.addUserMetadata(kvp.key.getStringValue(), kvp.value.getStringValue());
}
RE: Handling Map Parameters Passed to Custom Extension Instructions: Questions re Types - Added by Anthony Bufort 5 months ago
While what I had was tested and working, I have now refactored my code to use KeyValuePair as suggested.
Thank You! I sincerely appreciate the help,
-Tony
Please register to reply