I think I set up the namespace resolver right,
var nodes=xml.evaluate(path, //xpathExpression
xml, //contextNode
NSResolver, //namespaceResolver
XPathResult.ANY_TYPE, //resultType
null //result
);
and I think I am setting path correctly, (Ive tried alot of variation here, pretty much anything that might work)
path="/";
but the nodes.iterateNext() seems to be telling me I did something wrong :
firebug output : nodes : [object XPathResult] length : undefined
the x and xml object are good though, because I can see them and the xml in firebug. If you use my code, Im just testing in chrome so if youre using IE you may have a whole other can of worms. :)
Heres the xml (sanitized version)
<dataset xmlns="http://stub.test.data1" xmlns:xs="http://another.stub.test.moredata">
<!--
<dataset
xmlns="http://stub.test.data1"
xmlns:xs="http://another.stub.test.moredata"
xs:schemaLocation="http://yet.more.stub.test.data/xmldata.xsd"
>
-->
<metadata>
<item name="a" type="xs:string" length="92"/>
<item name="b" type="xs:string" length="50"/>
<item name="c" type="xs:short" precision="1"/>
<item name="d" type="xs:string" length="66"/>
<item name="e" type="xs:string" length="26"/>
<item name="f" type="xs:string" length="6"/>
<item name="g" type="xs:string" length="264"/>
<item name="h" type="xs:double" precision="2"/>
<item name="i" type="xs:string" length="22"/>
<item name="j" type="xs:date"/>
<item name="k" type="xs:date"/>
<item name="l" type="xs:string" length="16"/>
<item name="m" type="xs:short" precision="1"/>
<item name="n" type="xs:short" precision="1"/>
<item name="o" type="xs:string" length="50"/>
</metadata>
<data>
<row>
<value>someData1</value>
<value>someData2</value>
<value>someData3</value>
<value>someData4</value>
<value>someData5</value>
<value>someData6</value>
<value>someData7</value>
<value>someData8</value>
<value>someData9</value>
<value>someData10</value>
<value>someData11</value>
<value>someData12</value>
<value>someData13</value>
<value>someData14</value>
<value>someData15</value>
</row>
</data>
</dataset>
And heres the javascript :
function loadXMLDoc(dname)
{
if (window.XMLHttpRequest)
{
xhttp=new XMLHttpRequest();
}
else
{
xhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//initializes the request
xhttp.open("GET",dname,false);//method, url, optional async defaults to true
try {xhttp.responseType="msxml-document"} catch(err) {
console.log('hey, an error occured');
} // Helping IE
xhttp.send("");//send the request. Does not return till the response is returned (due to false above)
return xhttp;
}
function NSResolver(nsPrefix) {
console.log("nsPrefix : " + nsPrefix);
if(nsPrefix == "xs") {
return "http://www.w3.org/2001/XMLSchema-instance";
}
}
function displayNodes() {
// code for IE
if (window.ActiveXObject || xhttp.responseType=="msxml-document")
{
console.log('code for IE');
console.log('path=' + path);
xml.setProperty("SelectionLanguage","XPath");
nodes=xml.selectNodes(path);
for (i=0;i<nodes.length;i++)
{
document.write(nodes[i].childNodes[0].nodeValue);
document.write("<br>");
}
}
// code for Chrome, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument)
{
console.log('code for chr / ff / op');
console.log('path=' + path);
//docs : http://help.dottoro.com/ljruhkuj.php
var nodes=xml.evaluate(path, //xpathExpression
xml, //contextNode
NSResolver, //namespaceResolver
XPathResult.ANY_TYPE, //resultType
null //result
);
console.log("nodes : " + nodes + " length : " + nodes.length);
var result=nodes.iterateNext();
while (result)
{
document.write(result.childNodes[0].nodeValue);
document.write("<br>");
result=nodes.iterateNext();
}
}
document.write('shit should have displayed by now');
}
path="/";
function reload() {
x=loadXMLDoc("testxml.xml"); //x is now a XMLHttpRequest object
xml=x.responseXML; //xml is now a response to the request, or null if it failed
displayNodes();
console.log("x (XMLHTTPRequest Object) : " + x);
console.log("xml (XMLHTTPRequest.responseXML) : " + xml);
}
reload();
Looking at Mozilla's documentation for using XPath in Javascript, it seems that the XPathResult object has no such property as length that you're asking for.
So when firebug says length : undefined, that doesn't necessarily mean you've done something wrong in your XPath path or use of evaluate(). The only thing you've done wrong that I can see is to ask for nodes.length.
If your result were a snapshot, you could ask for nodes.snapshotLength, but it isn't:
When the result type in the resultType parameter is specified as ANY_TYPE, ... if the returned result type is a node-set then it will only be an UNORDERED_NODE_ITERATOR_TYPE.
Now when you iterate, you should get one result node: the document root node, that is, the (invisible) parent of the <dataset> element. Next, you're asking it to print result.childNodes[0].nodeValue. result.childNodes[0] should be the <dataset> element. The .nodeValue of an element is null, according to these docs. So presumably your document.write() is not showing anything.
Instead, try printing result.nodeName (docs here). This should give #document for the root node, or else the name of the element you've selected.
And if you're just trying to get something working, and verify that it's working, I would change your path to "/*". You'll get a more tangible result, namely, the <dataset> element.
Related
I am trying to make scrolling karaoke lyrics of a song. The lyric software outputs a code which contains time in seconds that I need to convert.
I Have two versions of the same song. One runs in 52 bpm (beats per minute) which is a slower version and another runs at 104 bpm which is faster version.
I have completed the first version which took hell lot of a time. I want to convert the code in such a way that I can get the faster version automatically.
Here is the sample code
<item dStartTime="4" dEndTime="8" n3DRhythm="2" str3DSceneLayoutFile="">
<text>Batti Gul Meter Chalu</text>
</item>
<item dStartTime="9.52" dEndTime="14.47" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj<10.44> ke<10.99> rulaya</text>
</item>
<item dStartTime="14.47" dEndTime="19.06" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj ke<15.94> hansaya</text>
</item>
I want to convert the same code as shown below where the time in seconds should be exactly half with two decimal points
<item dStartTime="2" dEndTime="4" n3DRhythm="2" str3DSceneLayoutFile="">
<text>Batti Gul Meter Chalu</text>
</item>
<item dStartTime="4.76" dEndTime="7.24" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj<5.22> ke<5.50> rulaya</text>
</item>
<item dStartTime="7.24" dEndTime="9.53" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj ke<7.97> hansaya</text>
</item>
I took the code in MS Excel and tried using substitute, concatenate and other few formulas to achieve what I need but I am unable to do so and though excel vba can help but I cannot do it in vba. Request you guys to help me out in this as I need to convert a lot of songs. Your kind assistance will save a lot of time. Any help in excel vba, javascript or any other way to replace the code.
In the <item> section, I need to convert the dStartTime and dEndTime
In the <text> section, I need to convert anything that looks like a number
Thank you
In Javascript, you can use DOMParser to convert the string into a document, after which you can iterate over each <item>, change the dStartTime and dEndTime attributes, and then change any numbers in the child <text> node:
const str = `<item dStartTime="4" dEndTime="8" n3DRhythm="2" str3DSceneLayoutFile="">
<text>Batti Gul Meter Chalu</text>
</item>
<item dStartTime="9.52" dEndTime="14.47" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj<10.44> ke<10.99> rulaya</text>
</item>
<item dStartTime="14.47" dEndTime="19.06" n3DRhythm="2" str3DSceneLayoutFile="">
<text>rajj ke<15.94> hansaya</text>
</item> `;
const doc = new DOMParser().parseFromString(str, 'text/html');
const halve = num => Math.round(100 * num / 2) / 100;
const halveAttrib = (node, name) => {
const newVal = halve(node.getAttribute(name))
node.setAttribute(name, newVal);
};
doc.querySelectorAll('item').forEach((item) => {
halveAttrib(item, 'dStartTime');
halveAttrib(item, 'dEndTime');
item.children[0].textContent = item.children[0].textContent
.replace(/\d+(\.\d+)/g, num => halve(num).toFixed(2));
});
const output = doc.body.innerHTML
.replace(/dstarttime/g, 'dStartTime')
.replace(/dendtime/g, 'dEndTime')
.replace(/n3drhythm/g, 'n3DRhythm')
.replace(/str3dscenelayoutfile/g, 'str3DSceneLayoutFile');
console.log(output);
This uses a DOM parser to write out the values and duration after reading each section of HTML from an html file. I struggled with accessing the Text nodes so have had to hack my way around that which I am not best pleased with. It does mean you know the value you are searching for which means you can do a replace on the html string by using regex .Replace method. For dStartTime and dendTime you can do a straight replace on the outerHTML. This replace just in the current HTMLDocument unless you write back out to file. SetAttribute doesn't work in this instance. I am not sure why I couldn't access the text nodes, particularly as don't have source HTMLDocument. Would be good to see if someone can solve that.
item.outerHTML = Replace$(item.outerHTML, "dstarttime=" & Chr$(34) & item.dstarttime & Chr$(34), "dstarttime=" & 666 & Chr$(34)) 'example 666 as replace value.
vba:
Option Explicit
Public Sub GetValues()
'VBE > Tools > References > HTML Object Library
Dim html As HTMLDocument
Debug.Print "doc1", vbNewLine
Set html = GetHTMLFileContent("C:\Users\User\Desktop\test.html") 'first html document. Info saved in text file with .html extension
GetItems html
Debug.Print "doc2", vbNewLine
Set html = GetHTMLFileContent("C:\Users\User\Desktop\test2.html") 'second html document. Info saved in text file with .html extension
GetItems html
End Sub
Public Sub GetItems(ByVal html As HTMLDocument)
Dim items As Object, item As Object, counter As Long
Set items = html.getElementsByTagName("item")
For Each item In items
Debug.Print " item " & counter + 1
Dim itemArr() As String
itemArr = Split(html.body.innerHTML, "</ITEM")
Debug.Print "startTime = " & Round(item.dStartTime, 2), "endTime = " & Round(item.dendTime, 2), "duration : " & Round(item.dendTime - item.dStartTime, 2)
Debug.Print "Associated text numbers "
On Error Resume Next
GetTextAttributeNumbers Split(itemArr(counter), "<TEXT>")(1)
On Error GoTo 0
counter = counter + 1
Next item
End Sub
Public Sub GetTextAttributeNumbers(ByVal inputString As String)
Dim matches As Object, iMatch As Object
With CreateObject("vbscript.regexp")
.Global = True
.MultiLine = True
.IgnoreCase = True
.Pattern = "\d{1,}(\.\d+)?"
If .TEST(inputString) Then
Set matches = .Execute(inputString)
For Each iMatch In matches
Debug.Print iMatch
Next iMatch
End If
End With
End Sub
Public Function GetHTMLFileContent(ByVal filePath As String) As HTMLDocument
'"C:\Users\HarrisQ\Desktop\test.html"
Dim fso As Object, hFile As Object, hString As String, html As New HTMLDocument
Set fso = CreateObject("Scripting.FileSystemObject")
Set hFile = fso.OpenTextFile(filePath)
Do Until hFile.AtEndOfStream
hString = hFile.ReadAll()
Loop
html.body.innerHTML = hString
Set GetHTMLFileContent = html
End Function
To properly parse the documents the following syntax is required:
Doc1
<html>
<head></head>
<body>
<item dstarttime="4" dendtime="8" n3drhythm="2" str3dscenelayoutfile="">
<text>
Batti Gul Meter Chalu
</text>
</item>
<item dstarttime="9.52" dendtime="14.47" n3drhythm="2" str3dscenelayoutfile="">
<text>
rajj<10.44> ke<10.99> rulaya
</text>
</item>
<item dstarttime="14.47" dendtime="19.06" n3drhythm="2" str3dscenelayoutfile="">
<text>
rajj ke<15.94> hansaya
</text>
</item>
</body>
</html>
Doc2
<html>
<head></head>
<body>
<item dstarttime="2" dendtime="4" n3drhythm="2" str3dscenelayoutfile="">
<text>
Batti Gul Meter Chalu
</text>
</item>
<item dstarttime="4.76" dendtime="7.24" n3drhythm="2" str3dscenelayoutfile="">
<text>
rajj<5.22> ke<5.50> rulaya
</text>
</item>
<item dstarttime="7.24" dendtime="9.53" n3drhythm="2" str3dscenelayoutfile="">
<text>
rajj ke<7.97> hansaya
</text>
</item>
</body>
</html>
This means, if not present, concantentating the missing start and end strings.
"<html><head></head><body>" & yourHTMLString & "</body>
</html>"
Example output from second document:
I am fetching an XML response from an external tool using
var myXML = api.path("item/"+itemId+"/metadata").dataType("xml").get();
The XML structure I receive this way looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MetadataListDocument xmlns="http://xml.vidispine.com/schema/vidispine">
<item id="VX-3594">
<metadata>
<revision>VX-61522,VX-61520,VX-61519,VX-61515,VX-61513,VX-61514,VX-61525,VX-61523</revision>
<group>Final Cut Server</group>
<timespan start="-INF" end="+INF">
<field uuid="2913190d-635c-4f81-951c-4ff3f934a547" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">
<name>shapeTag</name>
<value uuid="2c8b57d0-5c47-49c9-a4f0-54f7fef803cc" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">lowres</value>
<value uuid="6d994b0c-9d91-4b32-8052-824b3467a29e" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">original</value>
<value uuid="85cd07b7-6f13-4f6c-86fa-e42db20860c0" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">house_format_audio_through</value>
</field>
<field uuid="e2b4ca3c-6765-4890-99f4-f6e0624099eb" user="admin" timestamp="2015-03-16T16:47:57.506+01:00" change="VX-61513">
<name>portal_mf129489</name>
<value uuid="ab60ed17-75f4-4593-b068-9d8b6a700025" user="admin" timestamp="2015-03-16T16:47:57.506+01:00" change="VX-61513">no</value>
</field>
</timespan>
</metadata>
</item>
</MetadataListDocument>
Now I need to extract the value of the field with the name portal_mf129489, which in this case would be no.
To extract data from the xml output I already set the default name space:
default xml namespace = "http://xml.vidispine.com/schema/vidispine";
and I can extract things using
var myTimespan = myXML.item.metadata.timespan;
My question though is, how can I find a field element, which has a child element named portal_mf129489? If I find this element, I would need to fetch the value of the field's child element named value.
Each XML has multiple field elements, and inside the field I am looking for, name is always the first child element, value is always the second element.
Any ideas?
You can do this with little help of jQuery. The idea is to use jQuery to query XML document:
var xml = $.parseXML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><MetadataListDocument xmlns="http://xml.vidispine.com/schema/vidispine"><item id="VX-3594"><metadata><revision>VX-61522,VX-61520,VX-61519,VX-61515,VX-61513,VX-61514,VX-61525,VX-61523</revision><group>Final Cut Server</group><timespan start="-INF" end="+INF"><field uuid="2913190d-635c-4f81-951c-4ff3f934a547" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523"><name>shapeTag</name><value uuid="2c8b57d0-5c47-49c9-a4f0-54f7fef803cc" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">lowres</value><value uuid="6d994b0c-9d91-4b32-8052-824b3467a29e" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">original</value><value uuid="85cd07b7-6f13-4f6c-86fa-e42db20860c0" user="system" timestamp="2015-03-16T16:52:48.061+01:00" change="VX-61523">house_format_audio_through</value></field><field uuid="e2b4ca3c-6765-4890-99f4-f6e0624099eb" user="admin" timestamp="2015-03-16T16:47:57.506+01:00" change="VX-61513"><name>portal_mf129489</name><value uuid="ab60ed17-75f4-4593-b068-9d8b6a700025" user="admin" timestamp="2015-03-16T16:47:57.506+01:00" change="VX-61513">no</value></field></timespan></metadata></item></MetadataListDocument>');
var value = $(xml).find('field > name').filter(function(){ return $(this).text() === 'portal_mf129489' }).siblings('value').text();
$('body').append(value);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Okay, got it working, now. I added the function:
function getField(ts, name) {
for (var i = 0; i < ts.field.length(); i++) {
var f = ts.field[i];
if (f.name == name) {
return f;
}
}
}
and this:
var r = api.path("item/"+itemId+"/metadata").dataType("xml").get();
var metadata = r.item[0].metadata;
var ts = metadata.timespan[0];
var tag = getField(ts, 'portal_mf129489');
if (tag && tag.value) {
logger.log('value: '+tag.value[0]);
}
I have what should be an extremely simple xml question, which has me completely lost.
I have the following XML file:
<items>
<item percentFinished="0.0">
<details>
<name>Objective 1</name>
<description>Test objective</description>
</details>
</item>
<item percentFinished="0.0">
<details>
<name>Objective 2</name>
<description>Another objective</description>
</details>
<subitems>
<item percentFinished="0.0">
<details>
<name>Sub Objective 1</name>
<description>A Sub Objective</description>
</details>
</item>
</subitems>
</item>
</items>
The javascript file is eventually going to be creating dynamic html from the xml file, but for now I am just trying to get the parsing down properly. Here is my current javascript file:
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
} else {// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","defaultData.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
// HTML Div
var outer = document.createElement('div');
var div = document.createElement('div');
// Gets the item list from the XML
var itemList = selectValue(xmlDoc,"items/item");
var item = itemList.iterateNext();
while (item!=null) {
parseItemNode(item,div);
item = itemList.iterateNext();
}
outer.appendChild(div);
document.write(outer.innerHTML);
function parseItemNode(itemNode, parentNode) {
var details = selectValue(itemNode,"item/details").iterateNext();
var name = selectValue(details,"name").stringValue;
}
function selectValue(context,xpath) {
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
var nsResolver = document.createNSResolver( context.ownerDocument == null ? context.documentElement : context.ownerDocument.documentElement );
return document.evaluate(xpath, context, nsResolver, XPathResult.ANY_TYPE,null);
} else {
return context.selectNodes(xpath);
}
}
So the issue I am having is: the details variable in the parseItemNode() function is null.
I had a look at the selectValue(itemNode,"item/details") call in the debugger, and the result is this:
XPathResult {invalidIteratorState: false, resultType: 4, iterateNext: function, snapshotItem: function, ANY_TYPE: 0…}
booleanValue: [Exception: TypeError: Failed to read the 'booleanValue' property from 'XPathResult': The result type is not a boolean.]
invalidIteratorState: false
numberValue: [Exception: TypeError: Failed to read the 'numberValue' property from 'XPathResult': The result type is not a number.]
resultType: 4
singleNodeValue: [Exception: TypeError: Failed to read the 'singleNodeValue' property from 'XPathResult': The result type is not a single node.]
snapshotLength: [Exception: TypeError: Failed to read the 'snapshotLength' property from 'XPathResult': The result type is not a snapshot.]
stringValue: [Exception: TypeError: Failed to read the 'stringValue' property from 'XPathResult': The result type is not a string.]
__proto__: XPathResult
At this point, I have no idea what is going on or how to fix it, and I haven't been able to dig up anything on Google. Can someone explain to me what is happening, and how I should make it work correctly?
The details variable in the parseItemNode() function is null because the itemNode variable is the <item/> node itself.
Therefore, applying Xpath item/details on this <item/> will return the <details/> that are child of <item/> that are child of <item/>, hence, no node.
Switch the XPath expression to 'details' only since you're applying it on <item/> node.
Btw, XSLT is designed to build (X)HTML upon any XML source.
I am currently working with Jquery and my entire project needs to be done only using sharepoint Client Object Model (so i cant make use of server side coding). I have created a xml structure (by appending some string together) and stored it in a jquery var variable. Now my variable content looks like this
<Collection xmlns="http://schemas.microsoft.com/collection/metadata/2009"
xmlns:ui="http://schemas.microsoft.com/livelabs/pivot/collection/2009"
SchemaVersion="1" Name="listname">
<FacetCategories>
<FacetCategory Name="Title" Type="String" />
<FacetCategory Name="Created By" Type="String" />
<FacetCategory Name="Modified By" Type="String" />
</FacetCategories>
<Items ImgBase="http://460d87.dzc">
<Item Id="0" Img="#0" Name="Name1" Href="http://site/1_.000">
<Facets>
<Facet Name="Title">
<String Value="Name1" />
</Facet>
</Facets>
</Item>
</Items>
</collection>
I want to convert this variable in to xml content purely based on jquery.I have used ParseXml() Method but i'm not able to see the output in alert(). Please help me out with this.
Just use native built-in XML parser:
var parser, xml;
if (window.DOMParser) {
parser = new DOMParser();
xml = parser.parseFromString(str, "text/xml");
}
else { // IE
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = "false";
xml.loadXML(str);
}
var nodes = xml.getElementsByTagName('FacetCategory');
var i, l = nodes.length, items = [];
for (i = 0; i < l; i++) {
console.log(nodes[i].getAttribute('Name'));
}
http://jsfiddle.net/QqtMa/
Your xml is invalid, your root element is Collection but the closing tag is collection with small c, so the parser is failing
Hi I have this code that is all working in FF, except for the variable populated by a .text() method in IE.
The example code is below:
<script language="javascript" type="text/javascript">
$(document).find('f').each(function(){
var ff= $(this).attr("m");
var fmsg = $(this).text();
alert(ff + ' - ' +fmsg);
});
</script>
The data (document):
<data>
<f m="1">hi</f>
<f m="2">bye</f>
</data>
Why is the alert not showing '1 - hi' and '2 - bye', instead its showing an empty value for the fmsg variable. Any suggestions? Here is a working example: http://jsfiddle.net/dtuce/1/
The jQuery#text method doesn't really seem to be prepared to gather text content from XML from what I could see. (I'd gladly take pointers, though)
text: function( text ) {
if ( jQuery.isFunction(text) ) {
return this.each(function(i) {
var self = jQuery( this );
self.text( text.call(this, i, self.text()) );
});
}
if ( typeof text !== "object" && text !== undefined ) {
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
}
return jQuery.text( this );
}
There is -- as always -- a difference between the W3 adherent browsers and IE. The W3 compliant browsers expose the Node#textContent property, while IE exposes the Node#innerText property.
MDN documentation for Node#textContent
HTH,
FK