The given range with its front now at the requested entity if the path is valid; otherwise, an empty range is returned.
XMLParsingException on invalid XML.
1 { 2 auto xml = "<carrot>\n" ~ 3 " <foo>\n" ~ 4 " <bar>\n" ~ 5 " <baz/>\n" ~ 6 " <other/>\n" ~ 7 " </bar>\n" ~ 8 " </foo>\n" ~ 9 "</carrot>"; 10 11 auto range = parseXML(xml); 12 // "<carrot>" 13 assert(range.front.type == EntityType.elementStart); 14 assert(range.front.name == "carrot"); 15 16 range = range.skipToPath("foo/bar"); 17 // " <bar> 18 assert(!range.empty); 19 assert(range.front.type == EntityType.elementStart); 20 assert(range.front.name == "bar"); 21 22 range = range.skipToPath("baz"); 23 // " <baz/> 24 assert(!range.empty); 25 assert(range.front.type == EntityType.elementEmpty); 26 27 // other is not a child element of baz 28 assert(range.skipToPath("other").empty); 29 30 range = range.skipToPath("../other"); 31 // " <other/>" 32 assert(!range.empty); 33 assert(range.front.type == EntityType.elementEmpty); 34 } 35 { 36 auto xml = "<potato>\n" ~ 37 " <foo>\n" ~ 38 " <bar>\n "~ 39 " </bar>\n" ~ 40 " <crazy>\n" ~ 41 " </crazy>\n" ~ 42 " <fou/>\n" ~ 43 " </foo>\n" ~ 44 " <buzz/>\n" ~ 45 "</potato>"; 46 47 auto range = parseXML(xml); 48 // "<potato>" 49 assert(range.front.type == EntityType.elementStart); 50 51 range = range.skipToPath("./"); 52 // "<potato>" 53 assert(!range.empty); 54 assert(range.front.type == EntityType.elementStart); 55 assert(range.front.name == "potato"); 56 57 range = range.skipToPath("./foo/bar"); 58 // " <bar>" 59 assert(!range.empty); 60 assert(range.front.type == EntityType.elementStart); 61 assert(range.front.name == "bar"); 62 63 range = range.skipToPath("../crazy"); 64 // " <crazy>" 65 assert(!range.empty); 66 assert(range.front.type == EntityType.elementStart); 67 assert(range.front.name == "crazy"); 68 69 // Whether popFront is called here before the call to 70 // range.skipToPath("../fou") below, the result is the same, because 71 // both <crazy> and </crazy> are at the same level. 72 range.popFront(); 73 // " </crazy>" 74 assert(!range.empty); 75 assert(range.front.type == EntityType.elementEnd); 76 assert(range.front.name == "crazy"); 77 78 range = range.skipToPath("../fou"); 79 // " <fou/>" 80 assert(!range.empty); 81 assert(range.front.type == EntityType.elementEmpty); 82 } 83 // Searching stops at the first matching start tag. 84 { 85 auto xml = "<beet>\n" ~ 86 " <foo a='42'>\n" ~ 87 " </foo>\n" ~ 88 " <foo b='451'>\n" ~ 89 " </foo>\n" ~ 90 "</beet>"; 91 92 auto range = parseXML(xml); 93 range = range.skipToPath("foo"); 94 assert(!range.empty); 95 assert(range.front.type == EntityType.elementStart); 96 assert(range.front.name == "foo"); 97 98 { 99 auto attrs = range.front.attributes; 100 assert(attrs.front.name == "a"); 101 assert(attrs.front.value == "42"); 102 } 103 104 range = range.skipToPath("../foo"); 105 assert(!range.empty); 106 assert(range.front.type == EntityType.elementStart); 107 assert(range.front.name == "foo"); 108 109 { 110 auto attrs = range.front.attributes; 111 assert(attrs.front.name == "b"); 112 assert(attrs.front.value == "451"); 113 } 114 } 115 // skipToPath will work on an empty range but will always return an 116 // empty range. 117 { 118 auto range = parseXML("<root/>"); 119 assert(range.takeNone().skipToPath("nowhere").empty); 120 } 121 // Empty and absolute paths will also result in an empty range as will 122 // "../" without any actual tag name on the end. 123 { 124 auto range = parseXML("<root/>"); 125 assert(range.skipToPath("").empty); 126 assert(range.skipToPath("/").empty); 127 assert(range.skipToPath("../").empty); 128 } 129 // Only non-empty start tags have children; all other EntityTypes result 130 // in an empty range unless "../" is used. 131 { 132 auto xml = "<!-- comment -->\n" ~ 133 "<root>\n" ~ 134 " <foo/>\n" ~ 135 "</root>"; 136 auto range = parseXML(xml); 137 assert(range.skipToPath("root").empty); 138 assert(range.skipToPath("foo").empty); 139 140 range = range.skipToPath("../root"); 141 assert(!range.empty); 142 assert(range.front.type == EntityType.elementStart); 143 assert(range.front.name == "root"); 144 }
Treats the given string like a file path except that each directory corresponds to the name of a start tag. Note that this does not try to implement XPath as that would be quite complicated, and it really doesn't fit with a StAX parser.
A start tag should be thought of as a directory, with its child start tags as the directories it contains.
All paths should be relative. EntityRange can only move forward through the document, so using an absolute path would only make sense at the beginning of the document. As such, absolute paths are treated as invalid paths.
"./" and "../" are supported. Repeated slashes such as in "foo//bar" are not supported and are treated as an invalid path.
If range.front.type == EntityType.elementStart, then range._skiptoPath($(D_STRING "foo")) will search for the first child start tag (be it EntityType.elementStart or EntityType.elementEmpty) with the name "foo". That start tag must be a direct child of the current start tag.
If range.front.type is any other EntityType, then range._skipToPath($(D_STRING "foo")) will return an empty range, because no other EntityTypes have child start tags.
For any EntityType, range._skipToPath($(D_STRING "../foo")) will search for the first start tag with the name "foo" at the same level as the current entity. If the current entity is a start tag with the name "foo", it will not be considered a match.
range._skipToPath($(D_STRING "./")) is a no-op. However, range._skipToPath($(D_STRING "../")) will result in the empty range (since it doesn't target a specific start tag).
range._skipToPath($(D_STRING "foo/bar")) is equivalent to range._skipToPath($(D_STRING "foo"))._skipToPath($(D_STRING "bar")), and range._skipToPath($(D_STRING "../foo/bar")) is equivalent to range._skipToPath($(D_STRING "../foo"))._skipToPath($(D_STRING "bar")).