extract value from XML in pure JavaScript - javascript

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]);
}

Related

Replace time value inside a programming code using Excel VBA or Javascript

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:

Trouble using xpath to get stuff out of XML with javascript

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.

parse XML attribute value using javascript

How do I get the first url value in the 'media:content'.
Here is my XML format
<root>
<item>
<title>title1</title>
<media:group>
<media:content url="http://delivery_sva/1kqoy0pabup891733zq15sm2t3.mp4" />
<media:content url="http://delivery_sva/1kqoy0pabup891733zq15sm2t3.mp3" />
</media:group>
</item>
<item>
<title>title2</title>
<media:group>
<media:content url="http://delivery_sva/1kqoy0pabup891733zq15sm2t3.mp4" />
<media:content url="http://delivery_sva/1kqoy0pabup891733zq15sm2t3.mp3" />
</media:group>
</item>
</root>
I tried with the following code, but I am not able to getting the value of the attribute.
var doc = this.responseXML.documentElement;
var items = doc.getElementsByTagName("item");
for (var c = 0; c < items.length; c++) {
var item = items.item(c);
var sTitle1 = item.getElementsByTagName("title").item(0).text;
var itemswebportal = item.getElementsByTagName("media:content");
//currentwindow.sTitle1 is getting from the previous screen
if (sTitle1 == currentwindow.sTitle1) {
alert(itemswebportal[1].getAttribute("url"));
}
}
The problem is the : in the media:content. Try escaping it with two \. But I recommend using jquery for simpler manipulation and consistency across browsers.
Check the fiddle.

how to Convert a var to xml content using jquery

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

Attributes of XML node using Javascript

Is there a way to get the name of an attribute of an XML node using javascript.
Lets take this as a sample XML
<?xml version="1.0" encoding="UTF-8"?>
<Employees>
<Count name="EmployeeCount">100</Count>
<employee id="9999" >Harish</employee>
<Salary>
<year id="2000">50 Grands</year>
<year id="2001">75 Grands</year>
<year id="2002">100 Grands</year>
</Salary>
</Employees>
I am loading XML using ActiveXObject.As you can see not all elements have attributes.I need to list all attributes like
name
id
id
id
id
Try this:
var nodes = xml.selectNodes("//#*")
for(var i=0; i < nodes.length; i++)
{
alert(nodes[i].nodeName);
}

Categories