streamlet examples: xpath: support multiple modes (string, boolean, line-number, xpath), TODO: raw-xml
--- a/streamlet-examples/xpath.cpp Tue Jan 28 14:26:39 2020 +0100
+++ b/streamlet-examples/xpath.cpp Tue Jan 28 23:18:21 2020 +0100
@@ -36,11 +36,45 @@
for (Option o : getOptions(std::wregex(L"xmlns"), std::wregex(L"([^:]+):(.*)"))) ns[convertor.to_bytes(o.valueMatch[1])] = convertor.to_bytes(o.valueMatch[2]);
}
+ // Modes should share the logic of relpipe-in-xmltable
+
+ enum class Mode {
+ STRING,
+ BOOLEAN,
+ // TODO: support also XML number, when we have a rational or decimal numbers in Relational pipes
+ RAW_XML,
+ LINE_NUMBER,
+ XPATH
+ };
+
+ Mode toMode(std::wstring modeName) {
+ if (modeName == L"string") return Mode::STRING;
+ else if (modeName == L"boolean") return Mode::BOOLEAN;
+ else if (modeName == L"raw-xml") return Mode::RAW_XML;
+ else if (modeName == L"line-number") return Mode::LINE_NUMBER;
+ else if (modeName == L"xpath") return Mode::XPATH;
+ else throw std::invalid_argument("Unsupported mode: " + convertor.to_bytes(modeName));
+ }
+
+ std::wstring toType(Mode mode) {
+ if (mode == Mode::BOOLEAN) return BOOLEAN;
+ else if (mode == Mode::LINE_NUMBER) return INTEGER;
+ else return STRING;
+ }
+
+ // TODO: should not be done in particular streamlets but in the worker
+ std::wstring toNullValue(std::wstring type) {
+ if (type == BOOLEAN) return L"false";
+ else if (type == INTEGER) return L"0";
+ else return L"";
+ }
+
class XPathAttribute {
public:
std::wstring name;
std::wstring xpath;
+ Mode mode = Mode::STRING;
};
std::vector<XPathAttribute> xpathAttributes;
@@ -53,11 +87,13 @@
std::vector<AttributeMetadata> oam;
+ std::vector<Option> modeOptions = getOptions(L"mode");
std::vector<Option> attributeOptions = getOptions(L"attribute");
for (int i = 0, limit = attributeOptions.size(); i < limit; i++) {
+ Mode mode = i < modeOptions.size() ? toMode(modeOptions[i].value) : Mode::STRING;
std::wstring alias = getAlias(i, attributeOptions[i].value);
- xpathAttributes.push_back({alias, attributeOptions[i].value});
- oam.push_back({alias, STRING});
+ xpathAttributes.push_back({alias, attributeOptions[i].value, mode});
+ oam.push_back({alias, toType(mode)});
}
return oam;
@@ -72,12 +108,40 @@
xmlpp::Element* root = parser.get_document()->get_root_node();
for (XPathAttribute xpathAttribute : xpathAttributes) {
- // TODO: support various modes like in XMLTableCommand
- std::wstring result = convertor.from_bytes(root->eval_to_string(convertor.to_bytes(xpathAttribute.xpath), ns));
+ std::string xpath = convertor.to_bytes(xpathAttribute.xpath);
+ std::wstring result;
+ bool isNull = false;
+
+ if (xpathAttribute.mode == Mode::STRING) {
+ result = convertor.from_bytes(root->eval_to_string(xpath, ns));
+ } else if (xpathAttribute.mode == Mode::BOOLEAN) {
+ result = root->eval_to_boolean(xpath, ns) ? L"true" : L"false";
+ } else if (xpathAttribute.mode == Mode::LINE_NUMBER) {
+ xmlpp::NodeSet attributeNodes = root->find(xpath, ns);
+ if (attributeNodes.size()) {
+ result = std::to_wstring(attributeNodes[0]->get_line());
+ } else {
+ result = L"0";
+ isNull = true;
+ }
+ } else if (xpathAttribute.mode == Mode::XPATH) {
+ xmlpp::NodeSet attributeNodes = root->find(xpath, ns);
+ if (attributeNodes.size()) {
+ result = convertor.from_bytes(attributeNodes[0]->get_path());
+ } else {
+ result = L"";
+ isNull = true;
+ }
+ } else if (xpathAttribute.mode == Mode::RAW_XML) {
+ throw std::logic_error("Raw XML mode is not yet implemented."); // TODO: implement also RAW_XML
+ } else {
+ throw std::logic_error("Unsupported mode."); // should never happer
+ }
+
oa.push_back({result, false});
}
} catch (xmlpp::parse_error& e) {
- for (XPathAttribute xpathAttribute : xpathAttributes) oa.push_back({L"", true});
+ for (XPathAttribute xpathAttribute : xpathAttributes) oa.push_back({toNullValue(toType(xpathAttribute.mode)), true});
// invalid XML → xmlpp::parse_error → just skip this file
// invalid XPath → xmlpp::exception → failure
}