Java Convert InputStream of XML to Document object always results in NULL - java

I am trying to parse a XML string into org.w3c.dom.Document object.
I have looked at solutions provided here, here and a few other blogs that give a variation of the same solution. But the Document object's #Document variable is always null and nothing gets parsed.
Here is the XML file
XMLMappingValidator v = new XMLMappingValidator("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<mapping>\n" +
"<container>\n" +
"<source-container>c:\\stem.csv</source-container>\n" +
"<source-type>CSV_FILE</source-type>\n" +
"<target-container>db</target-container>\n" +
"<target-type>MySQL_TABLE</target-type>\n" +
"</container>\n" +
"<entity>\n" +
"<source-entity>stem</source-entity>\n" +
"<target-entity>tbl_stem</target-entity>\n" +
"</entity>\n" +
"<attributes>\n" +
"<attribute>\n" +
"<datatype>SMALLINT</datatype>\n" +
"<source-attribute>id</source-attribute>\n" +
"<target-attribute>id</target-attribute>\n" +
"</attribute>\n" +
"<attribute>\n" +
"<datatype>VARCHAR</datatype>\n" +
"<source-attribute>name</source-attribute>\n" +
"<target-attribute>name</target-attribute>\n" +
"</attribute>\n" +
"<attribute>\n" +
"<datatype>TEXT</datatype>\n" +
"<source-attribute>body</source-attribute>\n" +
"<target-attribute>body</target-attribute>\n" +
"</attribute>\n" +
"<attribute>\n" +
"<datatype>DATETIME</datatype>\n" +
"<source-attribute>date</source-attribute>\n" +
"<target-attribute>date</target-attribute>\n" +
"</attribute>\n" +
"<attribute>\n" +
"<datatype>VARCHAR</datatype>\n" +
"<source-attribute>info</source-attribute>\n" +
"<target-attribute>info</target-attribute>\n" +
"</attribute>\n" +
"</attributes>\n" +
"</mapping>");
The constructor XMLMappingValidator object's code is as follows
public class XMLMappingValidator {
private Document xml;
public XMLMappingValidator (String xmlString) throws
IOException, SAXException, ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
this.xml =
builder.parse(new InputSource(new StringReader(xmlString)));
}
public Document getXML() {
return xml;
}
}
When I call v.getXML().toString() I get [#document: null]
Clearly, the parse is failing. But I don't understand why.
Any suggestions/help is greatly appreciated.

Related

How to get key-value of XML in hashmap using java

I am facing a issue where I am not able to get the value in my hashmap as per the key of the XML while I am getting the parent key
Code I am using:
public static void main(String[] args) throws IOException {
try {
String XML="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<sj0:Msg>\r\n" +
" xmlns:sj0=\"http://fakesite.org/\"\r\n" +
" xmlns:sj1=\"http://fakesite.org/ind\"\r\n" +
" <sj0:Hdr>\r\n" +
" <sj2:Option>CreateTxn</sj2:Option>\r\n" +
" <sj2:ID>172246</sj2:ID>\r\n" +
" <sj2:CountryCode>CR</sj2:CountryCode>\r\n" +
" </sj0:Hdr>\r\n" +
" <sj0:ReqDet>\r\n" +
" <sj0:MReq>\r\n" +
" <sj0:qCore>\r\n" +
" <sj1:Reference>12345678</sj1:Reference>\r\n" +
" <sj1:BrnCode>CLM</sj1:BrnCode>\r\n" +
" <sj1:Source>M1T722</sj1:Source>\r\n" +
" <sj1:TxnLegCount>2</sj1:TxnLegCount>\r\n" +
" </sj0:qCore>\r\n" +
" </sj0:MReq>\r\n" +
" <sj0:LReq>\r\n" +
" <sj0:RCore>\r\n" +
" <sj1:Amt>19.28</sj1:Amt>\r\n" +
" <sj1:Dt>2019-09-04</sj1:Dt>\r\n" +
" <sj1:Date>2019-06-27</sj1:Date>\r\n" +
" </sj0:RCore>\r\n" +
" </sj0:LReq>\r\n" +
" <sj0:LReq>\r\n" +
" <sj0:RCore>\r\n" +
" <sj1:Ind>DC</sj1:Ind>\r\n" +
" <sj1:Currency>US</sj1:Currency>\r\n" +
" <sj1:LAmt>20.28</sj1:LAmt>\r\n" +
" </sj0:RCore>\r\n" +
" </sj0:LReq>\r\n" +
" </sj0:ReqDet>\r\n" +
"</sj0:Msg>";
String XString = XML;
System.out.println(XML);
HashMap<String, String> values = new HashMap<String, String>();
Document xml = convertStringToDocument(XString);
Node user = xml.getFirstChild();
NodeList childs = user.getChildNodes();
Node child;
for (int i = 0; i < childs.getLength(); i++) {
child = childs.item(i);
System.out.println(child.getNodeName());
System.out.println(child.getNodeType());
System.out.println(child.getUserData("Source"));
System.out.println(child.getTextContent());
values.put(child.getNodeName(), child.getTextContent());
}
System.out.println("Source name");
System.out.println(values.toString());
System.out.println(values.get("Source"));
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
private static Document convertStringToDocument(String xmlStr) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new ByteArrayInputStream(xmlStr.getBytes("UTF-8"))));
return doc;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Output:
{sj0:ReqDet=
12345678
CLM
M1T722
2
19.28
2019-09-04
2019-06-27
DC
US
20.28
, #text= , sj0:Hdr= CreateTxn 172246 CR }
I need to add Source in haspmap key not ReqDet.
I am facing issue to traverse through the XML.
Any idea where I am going wrong and also please explain how I can get the value of other keys-value i.e. Ind key of RCore parent key.
I am fine with any other approach or library to do achieve this task if this library have issue
below line of code having "*" which means it will select all nodes/key-values from the XML
NodeList nodeList = doc.getElementsByTagName("*");
Below is the full code works for me:
public static void main(String[] args) throws IOException {
String XML="YOUR XML";
HashMap<String, String> values =convertStringToDocument(XML);
System.out.println("values = "+values.get("sj1:Source"));
}
public static HashMap<String, String> convertStringToDocument(String xmlStr) {
HashMap<String, String> values = new HashMap<String, String>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = factory.newDocumentBuilder();
EntityResolver resolver = new EntityResolver() {
public InputSource resolveEntity(String publicId, String systemId) {
String empty = "";
ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());
System.out.println("resolveEntity:" + publicId + "|" + systemId);
return new InputSource(bais);
}
};
builder.setEntityResolver(resolver);
Document doc = builder.parse(new InputSource(new ByteArrayInputStream(xmlStr.getBytes("UTF-8"))));
NodeList nodeList = doc.getElementsByTagName("*");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
values.put(node.getNodeName(), node.getTextContent());
}
}
return values;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

JAVA, How can I edit the text inside a node, in xml?

so let's say I have the current XML file.
<?xml version="1.0" encoding="UTF-8"?>
<config>
<apps>
<app name="app1">
<url>someUrl</url>
<username>user1</username>
<password>qwerty123</password>
</app>
<app name="app2">
<url>someUrl</url>
<username>user2</username>
<password>asdasdasd</password>
</app>
<app name="app3">
<url>someUrl</url>
<username>user3</username>
<password>123456789</password>
</app>
</apps>
</config>
I've searched far and wide on how to edit the first password (qwerty123) to something else and then save the file, but I just can't find the proper solution.
Does anyone here have an idea on how can I do that?
Example solution with DOM parser:
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<config>\n" +
" <apps>\n" +
" <app name=\"app1\">\n" +
" <url>someUrl</url>\n" +
" <username>user1</username>\n" +
" <password>qwerty123</password>\n" +
" </app>\n" +
" <app name=\"app2\">\n" +
" <url>someUrl</url>\n" +
" <username>user2</username>\n" +
" <password>asdasdasd</password>\n" +
" </app>\n" +
" <app name=\"app3\">\n" +
" <url>someUrl</url>\n" +
" <username>user3</username>\n" +
" <password>123456789</password>\n" +
" </app>\n" +
" </apps>\n" +
"</config>";
//here use your InputStream for example from file
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(inputStream);
doc.getDocumentElement().normalize();
doc.getElementsByTagName("password").item(0).getFirstChild().setNodeValue("new content");
//here use your outputStream for example file output stream
OutputStream outputStream = new ByteArrayOutputStream();
Source xmlSource = new DOMSource(doc);
Result outputTarget = new StreamResult(outputStream);
TransformerFactory.newInstance().newTransformer()
.transform(xmlSource, outputTarget);
//when you replace output stream with file output stream remove this, as this line is only for debugging
System.out.println(new String(((ByteArrayOutputStream) outputStream).toByteArray()));
Maybe you can do that:
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<config>\r\n" +
" <apps>\r\n" +
" <app name=\"app1\">\r\n" +
" <url>someUrl</url>\r\n" +
" <username>user1</username>\r\n" +
" <password>qwerty123</password>\r\n" +
" </app>\r\n" +
" <app name=\"app2\">\r\n" +
" <url>someUrl</url>\r\n" +
" <username>user2</username>\r\n" +
" <password>asdasdasd</password>\r\n" +
" </app>\r\n" +
" <app name=\"app3\">\r\n" +
" <url>someUrl</url>\r\n" +
" <username>user3</username>\r\n" +
" <password>123456789</password>\r\n" +
" </app>\r\n" +
" </apps>\r\n" +
"</config>";
try {
SOAPMessage message = MessageFactory.newInstance().createMessage(null,new ByteArrayInputStream(xml.getBytes()));
System.out.println(message.getSOAPPart().getElementsByTagName("password").item(0).getTextContent());
message.getSOAPPart().getElementsByTagName("password").item(0).setTextContent("NewPassword");
System.out.println(message.getSOAPPart().getElementsByTagName("password").item(0).getTextContent());
StringWriter messageString = new StringWriter();
TransformerFactory.newInstance().newTransformer().transform(
new DOMSource(message.getSOAPPart()),
new StreamResult(messageString));
BufferedWriter writer = null;
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("file.xml", true), "utf-8"));
writer.write(messageString.toString());
writer.newLine();
writer.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Or you can just use the Marshaller and Unmarshaller, if you want I'll explain how to do it this way.

How to parse soap string in java

I have written the below code to parse a string in java but its not printing out a blank message.
How can I get specific parts from a SOAP message and get their values?
I want to get the error and the message in the request.
String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">"
+ "<S:Body>"
+ "<ns2:processResponse xmlns:ns2=\"http://ws.xxxxx.com/\">"
+ "<response><direction>response</direction>"
+ "<reference>09FG10021008111306320</reference>"
+ "<amount>0.0</amount>"
+ "<totalFailed>0</totalFailed>"
+ "<totalSuccess>0</totalSuccess>"
+ "<error>1</error>"
+ "<message>Invalid</message>"
+ "<otherReference>6360e28990c743a3b3234</otherReference>"
+ "<action>FT</action>"
+ "<openingBalance>0.0</openingBalance>"
+ "<closingBalance>0.0</closingBalance>"
+ "</response>"
+ "</ns2:processResponse>"
+ "</S:Body>"
+ "</S:Envelope>\n";
SAXBuilder builder = new SAXBuilder();
Reader in = new StringReader(xml);
Document doc = null;
Element root = null;
Element meta = null;
Element error = null;
Element status_message = null;
String status_code= "";
String message = "";
try
{
doc = builder.build(in);
root = doc.getRootElement();
meta = root.getChild("processResponse").getChild("response");
error = meta.getChild("error");
status_message = meta.getChild("message");
status_code = error.getText();
message = status_message.getText();
}catch (Exception e)
{
// do what you want
}
System.out.println("status_code: " + status_code + "\nmessage: " + message);
The response being generated is
status_code:
message:
You are making some mistakes in your code while picking up elements in xml. You can use this code and check,
public static void main(String[] args) {
String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<S:Body>"
+ "<ns2:processResponse xmlns:ns2=\"http://ws.xxxxx.com/\">"
+ "<response><direction>response</direction>" + "<reference>09FG10021008111306320</reference>"
+ "<amount>0.0</amount>" + "<totalFailed>0</totalFailed>" + "<totalSuccess>0</totalSuccess>"
+ "<error>1</error>" + "<message>Invalid</message>"
+ "<otherReference>6360e28990c743a3b3234</otherReference>" + "<action>FT</action>"
+ "<openingBalance>0.0</openingBalance>" + "<closingBalance>0.0</closingBalance>" + "</response>"
+ "</ns2:processResponse>" + "</S:Body>" + "</S:Envelope>\n";
SAXBuilder builder = new SAXBuilder();
Reader in = new StringReader(xml);
Document doc = null;
Element root = null;
Element error = null;
Element status_message = null;
String status_code = "";
String message = "";
try {
doc = builder.build(in);
root = doc.getRootElement();
Element body = root.getChild("Body", Namespace.getNamespace("S", "http://schemas.xmlsoap.org/soap/envelope/"));
Element processResponse = body.getChild("processResponse", Namespace.getNamespace("ns2", "http://ws.xxxxx.com/"));
Element response = processResponse.getChild("response");
error = response.getChild("error");
status_message = response.getChild("message");
status_code = error.getText();
message = status_message.getText();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("status_code: " + status_code + "\nmessage: " + message);
}
For me this is giving following output,
status_code: 1
message: Invalid

parsing Xml with NodeList and DocumentBuilder and getting values

I've followed almost all of the SO questions and answers, some make sense while others don't, i'm able to get some of my xml working some of the time. At this point I have a hobbled gob of nothing.
Below is my xml that i am trying work with
<GET_GUESS_CHART>
<sort_by_letter>
<letter_row>
<letter>A</letter>
<guess>16</guess>
<rank>3</rank>
</letter_row>
<letter_row>
<letter>B</letter>
<guess>5</guess>
<rank>1</rank>
</letter_row>
</sort_by_letter>
<sort_by_rank>
<letter_row>
<letter>A</letter>
<guess>16</guess>
<rank>1</rank>
</letter_row>
<letter_row>
<letter>E</letter>
<guess>15</guess>
<rank>2</rank>
</letter_row>
</sort_by_rank>
</GET_GUESS_CHART>
I want to loop through the document and loop through 'sort_by_letters' and 'sort_by_rank' and get values for each 'letter_row'.
Here is how i get the document:
URL url = new URL(Url[0]);
DocumentBuilderFactory dbf = DocumentBuilderFactory
.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// Download the XML file
Document doc = db.parse(new InputSource(url.openStream()));
doc.getDocumentElement().normalize();
I'm able to actually get the document, but for the life of me can not figure how to work it to get what i need.
All you need to do is to walk the DOM tree...
import java.io.ByteArrayInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class ReadXML {
private static final String XML = "<?xml version=\"1.0\"?>\n"
+ "<GET_GUESS_CHART>"
+ " <sort_by_letter>"
+ " <letter_row>"
+ " <letter>A</letter>"
+ " <guess>16</guess>"
+ " <rank>3</rank>"
+ " </letter_row>"
+ " <letter_row>" +
+ " <letter>B</letter>"
+ " <guess>5</guess>"
+ " <rank>1</rank>"
+ " </letter_row>"
+ " </sort_by_letter>"
+ " <sort_by_rank>"
+ " <letter_row>"
+ " <letter>A</letter>"
+ " <guess>16</guess>"
+ " <rank>1</rank>"
+ " </letter_row>"
+ " <letter_row>"
+ " <letter>E</letter>"
+ " <guess>15</guess>"
+ " <rank>2</rank>"
+ " </letter_row>"
+ " </sort_by_rank>"
+ "</GET_GUESS_CHART>";
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(new ByteArrayInputStream(XML.getBytes()));
NodeList rootElement = doc.getChildNodes();
NodeList sortByNodes = rootElement.item(0).getChildNodes();
for (int k = 0; k < sortByNodes.getLength(); k++) {
Node sortBy = sortByNodes.item(k);
System.out.println("SORT BY: " + sortBy.getNodeName());
NodeList letterRows = sortBy.getChildNodes();
for (int j = 0; j < letterRows.getLength(); j++) {
Node letterRow = letterRows.item(j);
NodeList letterRowDetails = letterRow.getChildNodes();
if (letterRowDetails.getLength() > 0) {
String letter = null;
String guess = null;
String rank = null;
for (int i = 0; i < letterRowDetails.getLength(); i++) {
Node detail = letterRowDetails.item(i);
if (detail.getNodeName().equals("letter")) {
letter = detail.getTextContent();
} else if (detail.getNodeName().equals("guess")) {
guess = detail.getTextContent();
} else if (detail.getNodeName().equals("rank")) {
rank = detail.getTextContent();
}
}
System.out.println("Letter=" + letter + ", guess=" + guess + ", rank=" + rank);
}
}
}
}
}
(You'll probably build an object and add it to some result list instead of the System.out line...)
OUTPUT:
SORT BY: #text
SORT BY: sort_by_letter
Letter=A, guess=16, rank=3
Letter=B, guess=5, rank=1
SORT BY: #text
SORT BY: sort_by_rank
Letter=A, guess=16, rank=1
Letter=E, guess=15, rank=2
To answer the comment: if you wanted to JUST get the "sort_by_letter" XML elements, you can add an extra if clause here...
...
for (int k = 0; k < sortByNodes.getLength(); k++) {
Node sortBy = sortByNodes.item(k);
if(sortBy.getNodeName().equals("sort_by_letter")) {
System.out.println("SORT BY: " + sortBy.getNodeName());
...

XPath and Java with Repeated Tags

I'm having some trouble parsing an XML file in Java. The file takes the form:
<root>
<thing>
<name>Thing1</name>
<property>
<name>Property1</name>
</property>
...
</thing>
...
</root>
Ultimately, I would like to convert this file into a list of Thing objects, which will have a String name (Thing1) and a list of Property objects, which will each also have a name (Property1).
I've been trying to use xpaths to get this data out, but when I try to get just the name for 'thing', it gives me all of the names that appear in 'thing', including those of the 'property's. My code is:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse(filename);
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression thingExpr = xpath.compile("//thing");
NodeList things = (NodeList)thingExpr.evaluate(dom, XPathConstants.NODESET);
for(int count = 0; count < things.getLength(); count++)
{
Element thing = (Element)things.item(count);
XPathExpression nameExpr = xpath.compile(".//name/text()");
NodeList name = (NodeList) nameExpr.evaluate(thing, XPathConstants.NODESET);
for(int i = 0; i < name.getLength(); i++)
{
System.out.println(name.item(i).getNodeValue());
}
}
Can anyone help? Thanks in advance!
You could try something like...
public class TestXPath {
public static void main(String[] args) {
String xml =
"<root>\n"
+ " <thing>\n"
+ " <name>Thing1</name>\n"
+ " <property>\n"
+ " <name>Property1</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property2</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property3</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property4</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property5</name>\n"
+ " </property>\n"
+ " </thing>/n"
+ " <NoAThin>\n"
+ " <name>Thing2</name>\n"
+ " <property>\n"
+ " <name>Property1</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property2</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property3</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property4</name>\n"
+ " </property>\n"
+ " <property>\n"
+ " <name>Property5</name>\n"
+ " </property>\n"
+ " </NoAThin>/n"
+ "</root>";
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());
Document dom = db.parse(bais);
XPath xpath = XPathFactory.newInstance().newXPath();
// Find the "thing" node...
XPathExpression thingExpr = xpath.compile("/root/thing");
NodeList things = (NodeList) thingExpr.evaluate(dom, XPathConstants.NODESET);
System.out.println("Found " + things.getLength() + " thing nodes...");
// Find the property nodes of thing
XPathExpression expr = xpath.compile("property");
NodeList nodes = (NodeList) expr.evaluate(things.item(0), XPathConstants.NODESET);
System.out.println("Found " + nodes.getLength() + " thing/property nodes...");
// Find all the property "name" nodes under thing
expr = xpath.compile("property/name");
nodes = (NodeList) expr.evaluate(things.item(0), XPathConstants.NODESET);
System.out.println("Found " + nodes.getLength() + " name nodes...");
System.out.println("Property value = " + nodes.item(0).getTextContent());
// Find all nodes that have property nodes
XPathExpression exprAll = xpath.compile("/root/*/property");
NodeList nodesAll = (NodeList) exprAll.evaluate(dom, XPathConstants.NODESET);
System.out.println("Found " + nodesAll.getLength() + " property nodes...");
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
Which will give you an output of something like
Found 1 thing nodes...
Found 5 thing/property nodes...
Found 5 name nodes...
Property value = Property1
Found 10 property nodes...
How about "//thing/name/text()" ?
The double slashes you have now before name mean "anywhere in the tree, not necessarily direct child nodes".
Use these XPath expressions:
//thing[name='Thing1']
this selects any thing element in the XML document, that has a name child whose string value is "Thing1".
also use:
//property[name='Property1']
this selects any property element in the XML document, that has a name child whose string value is "Property1".
Update:
To get all text nodes, each containing a string value of a thing element, just do:
//thing/text()
In XPath 2.0 one can get a sequence of the strings themselves, using:
//thing/string(.)
This isn't possible with a single XPath expression, but one can get the string value of a particular (the n-th)thing element like this:
string((//thing)[$n])
where $n must be substituted with a specific number from 1 to count(//thing) .
So, in your prograaming language, you can first determine cnt by evaluating this XPath expression:
count(//thing)
And then in a loop for $n from 1 to cnt dynamically produce the xpath expression and evaluate it:
string((//thing)[$n])
Exactly the same goes for obtaining all the values for property elements.

Categories