Read an XML file using node.js xpath xmldom - node.js

I'm attempting to select certain parts of an XML document using the node packages XPATH & XMLDOM, but I am getting nothing through for the element values. At a guess it's probably my XPATH definition, but to be honest I've no idea.
The top of my XML looks like the following:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="define2-0-0.xsl"?>
<ODM
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.cdisc.org/ns/odm/v1.3"
xmlns:def="http://www.cdisc.org/ns/def/v2.0"
ODMVersion="1.3.2"
FileType="Snapshot"
FileOID="StudyNum.ADaM-IG.1.0"
CreationDateTime="2018-02-08T09:40:51">
<Study OID="StudyNum.ADaM-IG.1.0">
<GlobalVariables>
<StudyName>StudyNum</StudyName>
<StudyDescription>Study Description</StudyDescription>
<ProtocolName>StudyName_PRCL_StudyNum</ProtocolName>
</GlobalVariables>
<MetaDataVersion OID="MDV.StudyNum.ADaM-IG.1.0" Name="Study StudyNum Data Definitions"
Description="Awful Syndrome"
def:DefineVersion="2.0.0"
and my code so far looks like the following:
var xpath = require('xpath'),
dom = require('xmldom').DOMParser,
fs = require('fs');
var xml = fs.readFileSync('./Define/define.xml', 'utf8').toString();
var select = xpath.useNamespaces({"xlink":"http://www.w3.org/1999/xlink", "ODM":"http://www.cdisc.org/ns/odm/v1.3", "def":"http://www.cdisc.org/ns/def/v2.0"});
var doc = new dom().parseFromString(xml)
console.log("test 1 : " + select('//ODM:Study/#OID', doc)[0].nodeValue);
console.log("test 2 : " + select('//ODM:Study/GlobalVariables/StudyName/', doc)[0].nodeValue);
The first test generates the expected result, but I just get an error with 'test 2'. Am I missing the obvious?
Thanks.

You simply forgot that namespaces defined with xmlns="..." on an element are inherited to the child nodes.
So the line xmlns="http://www.cdisc.org/ns/odm/v1.3" in your XML makes all the children having this (an ODM) namespace.
//ODM:Study/ODM:GlobalVariables/ODM:StudyName
Putting that in the whole expression it's
console.log("test 2 : " + select('//ODM:Study/ODM:GlobalVariables/ODM:StudyName', doc)[0].nodeValue);

Related

Can not get siblings from the start of a query xpath

I have the following structure of the XML file:
<body>
<content>
<text type="header">Header 1</text>
</content>
<content>
<text type="text">Text 1</text>
</content>
</body>
I need to get the first one content tag and then using that content element get his content tag sibling in node.js with select function.
Node.js xpath npm package.
What I am trying to do is:
allDocumentString is a string representation of all the XML
file.
const headerContentTag = select('//content/text[#type = 'header']', allDocumentString);
const textContentTag = select('//following-sibling::content', headerContentTagString);
But it does not work.
I need to exactly get the first one content tag and then depending on that tag the last one.
I know I can get the second one tag just without the first one, but I need the first one.
Strictly speaking, what you need is :
var doc = new dom().parseFromString(xml)
var nodes = xpath.select("//body/content[1][./text[#type='header']]/following-sibling::content[last()]", doc)
We first look for the first content element, child of body and which contains a text element with a #header attribute. Then get we get his last following-sibling.

How do I save an svg node to a file using nodejs?

I'm using d3 through a small helper package: https://www.npmjs.com/package/d3-node
I created my svg file using d3-node, here's the code.
const D3Node = require('d3-node')
var d3n = new D3Node()
var svg = d3n.createSVG()
.style("width","1920px")
.style("height","1080px")
.attr("preserveAspectRatio","true")
.html(firstTemplate)
.append("myCustomTag")
Now after this, I have no idea how to save the output. The main problem is in myCustomTag.
console.log(d3n.d3Element.select("svg").node().innerHTML)
This line is supposed to output my svg, and it does, except that myCustomTag becomes mycustomtagand my svg gets corrupt.
I have tried select("svg").node().outerHTML, select("svg").html(), innerText, I just couldn't find anything.
I can't use the innerHTML in this case, is there a way to store the svg file directly from the d3 variable?
You could use xmlserializer since SVG is XML and XML is case sensitive. Something like this works for me:
const D3Node = require('d3-node');
const xmlserializer = require('xmlserializer');
const d3n = new D3Node();
var svg = d3n.createSVG()
.style("width","1920px")
.style("height","1080px")
.attr("preserveAspectRatio","true");
svg.append("myCustomTag");
console.log(xmlserializer.serializeToString(svg.node()));
HTML is not case sensitive so when you try to use HTML methods to serialize things you won't necessarily get what you want.

How do I get the filename programmatically to use with this code?

The code below is the computed image source for an image control on my custom control. If I run it just like it stands it puts in photo.jpg and works fine. The problem is I want to pass that value programmatically. I have an upload control (fileUpload1) and a download control (fileDownload1) on the custom control as well. I've tried document1.photo which is where the jpg file is stored. I've tried several variations of currentdocument.getitemvalue, getitem, getattachments. But, so far, I have found no way to do this. In addition, I have tried setting the value of filename to the value of the upload control and the download control with no success.
I know this is probably simple, but, as a client developer, it's not simple to me.
Any help would be greatly appreciated.
var doc:NotesDocument = currentDocument.getDocument ();
var UID:string = doc.getUniversalID().toString();
var fp:string = database.getFilePath();
var filename = "photo.jpg"
return '/0/' + UID + '/$file/' + filename
Thanks,
MJ
There may be other ways to achieve this but here's what I usually do:
with image controls I need to be aware that computed resource URLs are in most cases prepended with the database's relative path ending with the database filename. So all I have to add is the usual file attachment URL as in
/0/docUnid/$File/imageFileName
If there is a chance that you have more than one file attached to your doc I need to find out which one of them is the one I wish to display. This can be done by looping through
currentDocument.getAttachmentList(fieldName)
which delivers a java.util.List of NotesEmbeddedObject elements. Using methods like .getName() I then can find the one file you're looking for.
EDIT:
quick example using a repeat to display multiple images sattached to a single richtext field (there may be better solutions esp. regarding the way to filter unwanted file types; I'm using file extensions, only allowing .jpg, .png and .gif). Images are displayed as simple html list elements:
<ul>
<xp:repeat id="repeat1" rows="30" var="pic">
<xp:this.value><![CDATA[#{javascript:
var att:java.util.List=docCar.getAttachmentList("Body");
var it=att.listIterator();
//new empty array, to be used as the repeater's datasource
var arr=[];
//build base path for each image file
var unid=docCar.getDocument().getUniversalID();
var p="/0/" + unid + "/$File/";
while(it.hasNext()){
var e:NotesEmbeddedObject=it.next();
if(e.getName().endsWithIgnoreCase(".png") ||
e.getName().endsWithIgnoreCase(".jpg") ||
e.getName().endsWithIgnoreCase(".gif")){
//push complete filepath + image filename to array
arr.push(p + e.getName());
}
}
return arr;}]]></xp:this.value>
<li>
<xp:image url="#{javascript:pic}" id="image1">
</xp:image>
</li>
</xp:repeat>
</ul>
Hope this helps
Well, I think I have it figured out. Here is the code that now works:
var doc:NotesDocument = currentDocument.getDocument ();
var UID:string = doc.getUniversalID().toString(); var fp:string =
database.getFilePath();
var filename = #AttachmentNames()
return '/0/' + UID + '/$file/' + filename
MJ

How to get the Structure/Template id by Structure/Template name

I have a requirement that, Need to create JournalArticle with Structure and Template.While creating JournalArticle the method expecting the StructureId and TemplateId but these are generated by Liferay.So by name how can i get Id's of both.
Create and execute a DynamicQuery, like so (just replace Template with Structure to get structures):
DynamicQuery q = DynamicQueryFactoryUtil.forClass(DDMTemplate.class)
.add(PropertyFactoryUtil.forName("name").like("%YOUR NAME%"));
List<DDMTemplate> templates = DDMTemplateLocalServiceUtil.dynamicQuery(q);
You have to use like since the names of the structures/templates are saved like so:
<?xml version='1.0' encoding='UTF-8'?>
<root available-locales="de_DE" default-locale="de_DE">
<Name language-id="de_DE">YOUR NAME</Name>
</root>
There can be different names for different locales.
You can get StructureId (called DDMStructure) with this code
long classNameIdJournalArticle = ClassNameLocalServiceUtil.getClassNameId(JournalArticle.class);
DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(groupId, classNameIdJournalArticle, "myDDMStructureName");
And TemplateId (called DDMTemplate) with this code
DDMTemplate ddmTemplate = DDMTemplateLocalServiceUtil.getTemplate(groupId, classNameIdDDMStructure, "ddmTemplateName");

JSON.Net SerializeXnode excluding certain nodes

I have a xml string that i'm trying to convert to JSON using JSON.Net. The problem is that i want only certain part of this xml in my JSON string. Below is the code i use and what i need.
var x = XDocument.Parse(xmlString);
var json = JsonConvert.SerializeXNode(x);
This will convert the whole doc. This is how json string looks like in a JSON Viewer
What i want is ONLY the table (The Arrowed one in image 1) and its descendants to be inside string json.
Is it possible? How to achieve it? Can i use a custom ContractResolver with SerializeXnode?
You've got an XDocument, so why not simply select the part you want and then serialize just that part?
Try something like this:
var doc = XDocument.Parse(xmlString);
var table = doc.XPathSelectElement("//table[#class=\"form\"]");
var json = JsonConvert.SerializeXNode(table);
Note that XPathSelectElement is an extension method, so you will need using System.Xml.XPath; at the top of your code if you don't already have it.
EDIT
You can do it without XPath like this:
var doc = XDocument.Parse(xmlString);
var table = root.Descendants(XName.Get("table"))
.Where(e => e.Attributes(XName.Get("class"))
.Select(a => a.Value)
.FirstOrDefault() == "form")
.First();
var json = JsonConvert.SerializeXNode(table);
Both approaches give the same results, the table plus all descendants.

Resources