The following function parses an XML-style attribute assignment in the form "qname='value'", where qname
can be a qualified name in the form, "prefix:localName". The prefix (if any) must be registered in the specified prefixStack
object. Either single or double quotes may be used to enclose the attribute value. The parsed attribute is stored in the specified attribute
object of type balxml::ElementAttribute
. Note that this function modifies the input string by inserting null characters, rather than copying the component parts into allocated memory. This is a realistic interface for a function used within an XML parser that has already copied the XML stream into allocated memory. Also note that this function does not interpret character escapes such as "&". First, find the end of the qualified name, i.e., the =
character: char *qname = attributeString;
char *equalPtr = bsl::strchr(qname, '=');
if (0 == equalPtr) {
return -1;
}
Then find out which quote character is used to start the value string char quote = *(equalPtr + 1);
if (quote != '\'' && quote != '"') {
return -1;
}
The value string starts after the opening quote and extends until a matching quote: char *value = equalPtr + 2;
char *endValue = bsl::strchr(value, quote);
if (0 == endValue) {
return -1;
}
Once we have successfully parsed the string, chop it into pieces by putting a null terminator at the end of the qualified name and at the end of the value string: *equalPtr = '\0';
*endValue = '\0';
Use the prefix stack, qname, and value to set the attribute object. All other arguments are defaulted and will be computed as needed. attribute->reset(prefixStack, qname, value);
return 0;
}
Before calling the parseAttribute
function, it is necessary to create a namespace registry and prefix stack, as well as to register one or more prefixes: int main()
{
balxml::NamespaceRegistry registry;
balxml::PrefixStack prefixes(®istry);
int cal = prefixes.pushPrefix("cal",
"http://www.bloomberg.com/schemas/calendar");
prefixes.pushPrefix("math", "http://www.bloomberg.com/schemas/math");
prefixes.pushPrefix("",
"http://www.bloomberg.com/schemas/app");
Now we can parse an attribute string and the balxml::ElementAttribute
object will provide detailed information about it. char attrStr1[] = "cal:date='12-07-2006'";
balxml::ElementAttribute attribute1;
assert(attribute1.isNull());
parseAttribute(&attribute1, attrStr1, &prefixes);
assert(0 == bsl::strcmp("cal:date", attribute1.qualifiedName()));
assert(0 == bsl::strcmp("12-07-2006", attribute1.value()));
assert(0 == bsl::strcmp("date", attribute1.localName()));
assert(0 == bsl::strcmp("cal", attribute1.prefix()));
assert(cal == attribute1.namespaceId());
assert(0 == bsl::strcmp("http://www.bloomberg.com/schemas/calendar",
attribute1.namespaceUri()));
assert(0 == attribute1.flags());
Results are slightly different when the attribute name has no prefix: char attrStr2[] = "name=\"Bloomberg, L.P.\"";
balxml::ElementAttribute attribute2;
parseAttribute(&attribute2, attrStr2, &prefixes);
assert(0 == bsl::strcmp("name", attribute2.qualifiedName()));
assert(0 == bsl::strcmp("Bloomberg, L.P.", attribute2.value()));
assert(0 == bsl::strcmp("name", attribute2.localName()));
As per the XML namespace standard, an attribute with no namespace prefix does NOT inherit the default namespace but rather has NO namespace: assert(0 == bsl::strcmp("", attribute2.prefix()));
assert(-1 == attribute2.namespaceId());
assert(0 == bsl::strcmp("", attribute2.namespaceUri()));
assert(0 == attribute2.flags());
A balxml::ElementAttribute
does not need to be generated by parsing XML code. If a specific facet of the object is set to a non-null value, then that value will be returned by the corresponding accessor even if it is inconsistent with the other values in the object. For example, the following constructs a valid balxml::ElementAttribute
object, even though the prefix value does not agree with the qualified name: There is no consistency checking, and the mismatched prefix is simply returned by the accessor: assert(0 == bsl::strcmp("math:product", attribute3.qualifiedName()));
assert(0 == bsl::strcmp("product", attribute3.localName()));
assert(0 == bsl::strcmp("cal", attribute3.prefix()));
Note that the ability to create inconsistent objects is a deliberate feature. It allows parsers to construct balxml::Attribute
objects before all information is known, e.g., before the namespace is registered with the prefix stack. Consistency checking also reduces performance.