How to insert schemalocation in a xml document via DOM - dom

i create a xml document with JAXP and search a way to insert the schemalocation.
At the moment my application produces:
<?xml version="1.0" encoding="UTF-8"?>
<root>
...
</root>
But i need:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="namespaceURL pathToMySchema.xsd">
...
</root>
My code:
StreamResult result = new StreamResult(writer);
Document doc = getDocument();
Transformer trans = transfac.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "yes");
trans.setOutputProperty(OutputKeys.METHOD, "xml");
trans.setOutputProperty(OutputKeys.VERSION, "1.0");
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
DOMSource source = new DOMSource(depl.getAsElement(doc));
trans.transform(source, result);
Thanks for your time,
Kasten

In XML data model namespace nodes are not actually read from parent element but each element has its own namespace nodes. Therefore simply adding a new default namespace to root element doesn't work but results in a document like this
<root xmlns="namespaceURL">
<child xmlns=""/>
...
</root>
Notice the appearing of empty default namespace xmlns="" on the child element(s). What actually needs to be done is to modify the namespace of every node or to create a new document with the desired default namespace and copy the contents, element and attribute names etc. of the old document to the new one. These can be done by recursively going through the original document. With Java DOM implementation this can be laborious, I've heard. One short cut might be to read the document with a namespace-unaware DOM and then add as attribute the new default namespace. Other solution is to change the namespace with an XSLT transformation, which seems quite suitable in this case, since you actually are already generating the output via XSLT transformation.
Use this XSLT stylesheet to add a new default namespace and the schema location to the root element. This stylesheet preserves old namespaces but adds all elements to new default namespace if they previously were in no-namespace.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- Template to add a default namespace to a document -->
<!-- Elements without a namespace are "moved" to default namespace -->
<!-- Elements with a namespace are copied as such -->
<!-- string for default namespace uri and schema location -->
<xsl:variable name="ns" select="'namespaceURL'"/>
<xsl:variable name="schemaLoc" select="'namespaceURL pathToMySchema.xsd'"/>
<!-- template for root element -->
<!-- adds default namespace and schema location -->
<xsl:template match="/*" priority="1">
<xsl:element name="{local-name()}" namespace="{$ns}">
<xsl:attribute name="xsi:schemaLocation"
namespace="http://www.w3.org/2001/XMLSchema-instance">
<xsl:value-of select="$schemaLoc"/>
</xsl:attribute>
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!--template for elements without a namespace -->
<xsl:template match="*[namespace-uri() = '']">
<xsl:element name="{local-name()}" namespace="{$ns}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<!--template for elements with a namespace -->
<xsl:template match="*[not(namespace-uri() = '')]">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--template to copy attributes, text, PIs and comments -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Instead of creating the transformer with
Transformer trans = transfac.newTransformer();
(which creates and stylesheet that does an identy transformation), create an XSLT input source and give it as a parameter to newTransformer()
javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource(xsltFile);
Transformer trans = transFact.newTransformer(xsltSource);
where xsltFile is a File object pointing to that XSLT file.
Set the output properties as you wish and call transform() as in your sample code. The result should be what you desired, but I have not tested this in Java. The given XSLT file is tested for some trivial cases and there is a sample input and output at the end of this answer.
Some minor notes:
The original document object is not modified in this process. The new default namespace only appears in the output of the transform() method.
The namespace prefix for schema-instance namespace is usually xsi:, not xs: as in your example code (xs: is used in schema definitions (as well as xsd:)) .
Sample input and output for the XSLT stylesheet shown above
Input:
<root>
<child>text</child>
<child attribute="attr-value"/>
<?pi-target pi-content?>
<nsx:ns-child xmlns:nsx="ns1x">
<no-ns-child>text</no-ns-child>
<!-- comment -->
<nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
</nsx:ns-child>
<defns-child xmlns="default-ns">
<def-child attr="val">text</def-child>
<child xmlns=""/>
</defns-child>
<child>text</child>
</root>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="namespaceURL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="namespaceURL pathToMySchema.xsd">
<child>text</child>
<child attribute="attr-value"/>
<?pi-target pi-content?>
<nsx:ns-child xmlns:nsx="ns1x">
<no-ns-child>text</no-ns-child>
<!-- comment -->
<nsx:ns-child nsx:ns-attribute="nsx-attr-value">text</nsx:ns-child>
</nsx:ns-child>
<defns-child xmlns="default-ns">
<def-child attr="val">text</def-child>
<child xmlns="namespaceURL"/>
</defns-child>
<child>text</child>
</root>

You can add the namespaces in the root when creating the document.
String NS_URL = "namespaceURL";
doc = builder.newDocument();
Element root = doc.createElementNS(NS_URL, "root");
root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance",
"xs:schemaLocation", NS_URL + " pathToMySchema.xsd");
doc.appendChild(root);
Then for each element added to the doc instead of createElement() use createElementNS()
doc.createElementNS(NS_URL, name);
This results in what you were looking for.
<root
xmlns="namespaceURL"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="namespaceURL pathToMySchema.xsd"
>

Here's how to give a hint to the parser in order to solve your problem:
http://bytes.com/topic/java/answers/16892-xerces-how-perfrom-schema-validations-without-using-xsi-schemalocation
It goes like this:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(true);
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLocation",
"http://www.example.com/Report.xsd");
Here is a validation example with some source code. It might help you.
http://www.ibm.com/developerworks/xml/library/x-tipvalschm/
(If all comes to worse, you can always search-and-replace. I know it is not the ideal solution, but the javax.xml.transform.OutputKeys doesn't seem to have a member related to the schemalocation attribute.)

Related

How in Powershell see all XML Levels

I have a test xml and I want to get the value from this line ATTRIBUTE NAME="News- offers_OPT_EMAIL">F
so I can check for the value F or T
if I do below I can get the title but how do I get the above line value.
[xml]$xml = Get-Content testFile.xml
$xml
$xml.CUSTOMERS.CUSTOMER.NAME.TITLE
sample XML code
<?xml version="1.0" encoding="UTF-8"?>
<CUSTOMERS xml:lang="en">
<CUSTOMER CREATED_DATE="2018-01-01 05:18:53.0" GROUP_ID="" ID="95656565">
<NAME>
<TITLE>M</TITLE>
<FIRST_NAME>Joe</FIRST_NAME>
<LAST_NAME>Smith</LAST_NAME>
</NAME>
<GENDER/>
<DATE_OF_BIRTH/>
<ADDRESS>
<ADDRESS_LINE_1>1 White House</ADDRESS_LINE_1>
<ADDRESS_LINE_2>Old Ave</ADDRESS_LINE_2>
<ADDRESS_LINE_3/>
<TOWNCITY>LONDON</TOWNCITY>
<COUNTY/>
<POSTCODE>18659</POSTCODE>
<COUNTRY>France</COUNTRY>
</ADDRESS>
<ADDRESS>
<ADDRESS_LINE_1>175 avenue de la division Leclerc</ADDRESS_LINE_1>
<ADDRESS_LINE_2/>
<ADDRESS_LINE_3/>
<TOWNCITY>Antony</TOWNCITY>
<COUNTY/>
<POSTCODE>92160</POSTCODE>
<COUNTRY>France</COUNTRY>
</ADDRESS>
<CONTACT_DETAILS>
<TELEPHONE MARKETING_OPT_IN="F" TYPE="MOBILE">0123456789</TELEPHONE>
<EMAIL MARKETING_OPT_IN="F">johnsmith#gmail.com</EMAIL>
</CONTACT_DETAILS>
<ATTRIBUTE NAME="News- offers_OPT_EMAIL">F</ATTRIBUTE>
<NOTE>NA</NOTE>
</CUSTOMER>
You could use SelectSingleNode or SelectNodes with an XPath expression. There are several options to achieve what you want, depending on your intention, but this would be one way to do it:
# finde the nodes
$nodes = $xml.SelectNodes("//*[local-name()='ATTRIBUTE'][#NAME='News- offers_OPT_EMAIL']")
# get value
$nodes.InnerText
Or if the value of the attribute doesn't matter, simply do:
$xml.customers.customer.attribute.'#text'

SWXMLHash can't parse a [XMLIndexer]

I am trying for first time to parse a XML file and I am using SWXMLHash.
My xml file is:
<weatherdata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://api.met.no/weatherapi/locationforecast/1.9/schema" created="2018-02-13T18:12:31Z">
<meta>
<model name="LOCAL" termin="2018-02-13T12:00:00Z" runended="2018-02-13T14:27:32Z" nextrun="2018-02-13T22:00:00Z" from="2018-02-13T19:00:00Z" to="2018-02-16T06:00:00Z" />
<model name="EPS" termin="2018-02-13T00:00:00Z" runended="2018-02-13T09:03:05Z" nextrun="2018-02-13T22:00:00Z" from="2018-02-16T12:00:00Z" to="2018-02-22T18:00:00Z" />
</meta>
<product class="pointData">
<time datatype="forecast" from="2018-02-13T19:00:00Z" to="2018-02-13T19:00:00Z">
<location altitude="0" latitude="" longitude="">
<temperature id="TTT" unit="celsius" value="3.5"/>
<windDirection id="dd" deg="158.8" name="S"/>
<windSpeed id="ff" mps="8.8" beaufort="5" name="Frisk bris"/>
<windGust id="ff_gust" mps="15.0"/>
<areaMaxWindSpeed mps="13.8"/>
<humidity value="82.1" unit="percent"/>
<pressure id="pr" unit="hPa" value="1002.5"/>
<cloudiness id="NN" percent="99.3"/>
<fog id="FOG" percent="0.0"/>
<lowClouds id="LOW" percent="73.2"/>
<mediumClouds id="MEDIUM" percent="0.0"/>
<highClouds id="HIGH" percent="91.0"/>
<dewpointTemperature id="TD" unit="celsius" value="0.6"/>
</location>
</time>
The <time> </time> repeates.
Having made a let parsedResultXML = SWXMLHash.parse(data)
I am trying to parse with a for in loop
for node in parsedResultXML["weatherdata"]["product"]["time"].all{
print(node)
}
I get results
<time to="2018-02-14T01:00:00Z" from="2018-02-14T01:00:00Z" datatype="forecast">
<location latitude="" longitude="" altitude="0">
<temperature id="TTT" unit="celsius" value="3.4"></temperature>
<windDirection id="dd" name="S" deg="158.7"></windDirection>
<windSpeed name="Liten kuling" mps="11.1" id="ff" beaufort="6"></windSpeed>
<windGust id="ff_gust" mps="19.0"></windGust>
<areaMaxWindSpeed mps="17.5"></areaMaxWindSpeed>
<humidity unit="percent" value="88.3"></humidity>
<pressure id="pr" unit="hPa" value="1002.5"></pressure>
<cloudiness id="NN" percent="99.3"></cloudiness>
<fog id="FOG" percent="0.1"></fog>
<lowClouds id="LOW" percent="98.6"></lowClouds>
<mediumClouds id="MEDIUM" percent="93.4"></mediumClouds>
<highClouds id="HIGH" percent="57.8"></highClouds>
<dewpointTemperature id="TD" unit="celsius" value="1.5"></dewpointTemperature>
</location>
</time>
But I can't access the elements.
Using
for node in parsedResultXML["weatherdata"]["product"]["time"].all{
node["time"].element?.attribute(by: "from")?.text
}
I don't get any results back.
Any idea??
Thanks
You're close... as #rmaddy commented, node is already indexed to "time". So instead of:
for node in parsedResultXML["weatherdata"]["product"]["time"].all {
node["time"].element?.attribute(by: "from")?.text
}
Do this instead:
for node in parsedResultXML["weatherdata"]["product"]["time"].all {
node.element?.attribute(by: "from")?.text
}

DocuSign API not asking or setting the dateSigned though it is present in the xml

Having followed the example found earlier here on StackOverflow (dateSignedTabs does not work), I still fail to get a signature date to appear. Here's a snippet of the xml.
<?xml version="1.0" encoding="UTF-8"?>
<envelopeDefinition xmlns="http://www.docusign.com/restapi">
<emailSubject>Boiler Contract -00006)</emailSubject>
<status>sent</status>
<documents>
<document>
<documentId>1</documentId>
<name>VDBK SW6-CSIK-0001.pdf</name>
</document>
</documents>
<recipients>
<signers>
<signer>
<recipientId>1</recipientId>
<routingOrder>1</routingOrder>
<note>Team</note>
<email>joebloggs#gmail.com</email>
<name>Eric Test6-1</name>
<tabs>
<signHereTabs>
<signHere>
<xPosition>58</xPosition>
<yPosition>617</yPosition>
<documentId>1</documentId>
<pageNumber>1</pageNumber>
</signHere>
</signHereTabs>
<dateSignedTabs>
<dateSigned>
<xPosition>58</xPosition>
<yPosition>440</yPosition>
<tabLabel>Date Signed</tabLabel>
<name>Date Signed</name>
<documentId>1</documentId>
<pageNumber>1</pageNumber>
<recipientId>1</recipientId>
</dateSigned>
</dateSignedTabs>
</tabs>
</signer>
</signers>
</recipients>
</envelopeDefinition>
Can anybody spot why this is off?

Orbeon control details in own component

I'm making my own component, and I want to have oportunity to set some properties in form builder. In all I want to reach effect similar to autocomplete control, where I can set 3 properties (URI, xpath, and relative xpath). I read, that I can do it using control-details markup, but unfortunately it does not work. This is code (I working on davinci tutorial):
<xbl:binding element="fr|tutorial-davinci" id="fr-tutorial-davinci" xxbl:mode="lhha binding value">
<metadata xmlns="http://orbeon.org/oxf/xml/form-builder" xmlns:xf="http://www.w3.org/2002/xforms">
<display-name lang="en">davinci-modified</display-name>
<templates>
<instance label=""/>
<view>
<fr:tutorial-davinci id="" appearance="minimal" labelref="#label" xmlns="" resource="" >
<xf:label ref=""/>
<xf:hint ref=""/>
<xf:help ref=""/>
<xf:alert ref=""/>
</fr:tutorial-davinci>
</view>
</templates>
<control-details>
<xf:input>
<xf:label lang="en">Some param</xf:label>
</xf:input>
</control-details>
</metadata>
<xbl:template>
<xf:model>
<xf:instance id="id1"><value/></xf:instance>
</xf:model>
<xf:input ref="instance('id1')" />
</xbl:template>
</xbl:binding>
You must have a ref attribute on<xf:input> which points to an attribute or element where the information will be stored. In autocomplete.xml, there is in particular theresource` attribute, which:
is pointed to by <xf:input>
is present on the <fr:autocomplete> template

Orbeon autocomplete - Content is not allowed in prolog

I'm trying to add an autocomplete component in dynamic mode to a form created with the Orbeon Form Builder (version Orbeon Forms 3.9.0+.stable.201109261742 PE).
The xml below is a simple test form with the autocomplete that retrieves the data from a public webservice (currently with a static request body).
The first time the autocomplete loads the data it works just fine: the items are presented in a list. But the second time, when I type another character to narrow down the search results, or when I select an item from the list, I get the error message "Content is not allowed in prolog".
If I ignore the error and continue anyway, the error changes to "Got unexpected request sequence number".
I don't understand why it only works the first time since the data returned by the web service is always the same because of the static request body.
Below you find the complete xml of the test form.
<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:saxon="http://saxon.sf.net/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:exforms="http://www.exforms.org/exf/1-0"
xmlns:sql="http://orbeon.org/oxf/xml/sql"
xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xxforms="http://orbeon.org/oxf/xml/xforms"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xhtml:head>
<xhtml:title>Test form</xhtml:title>
<xforms:model id="fr-form-model">
<xforms:instance id="fr-form-instance">
<form>
<section-1>
<codeSearch/>
</section-1>
</form>
</xforms:instance>
<xforms:bind id="fr-form-binds" nodeset="instance('fr-form-instance')">
<xforms:bind id="section-1-bind" nodeset="section-1">
<xforms:bind id="codeSearch-bind" nodeset="codeSearch" name="codeSearch" type="xforms:string"/>
</xforms:bind>
</xforms:bind>
<xforms:instance id="fr-form-metadata" xxforms:readonly="true">
<metadata>
<application-name>IRISbox</application-name>
<form-name>Test</form-name>
<title xml:lang="fr">Test form</title>
<description xml:lang="fr"/>
<author/>
<logo mediatype="" filename="" size=""/>
</metadata>
</xforms:instance>
<xforms:instance id="fr-form-attachments">
<attachments>
<css mediatype="text/css" filename="" size=""/>
<pdf mediatype="application/pdf" filename="" size=""/>
</attachments>
</xforms:instance>
<xforms:instance id="fr-form-resources" xxforms:readonly="false">
<resources>
<resource xml:lang="fr">
<codeSearch>
<label>Cities</label>
<hint/>
<help/>
<alert/>
</codeSearch>
<section-1>
<label>Test</label>
<help/>
</section-1>
</resource>
</resources>
</xforms:instance>
<xforms:instance id="fr-service-request-instance" xxforms:exclude-result-prefixes="#all">
<request/>
</xforms:instance>
<xforms:instance id="fr-service-response-instance" xxforms:exclude-result-prefixes="#all">
<response/>
</xforms:instance>
<!-- Search Cities by Name HTTP service -->
<xforms:instance id="searchCitiesByName-instance" class="fr-service"
xxforms:exclude-result-prefixes="#all">
<body><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.restfulwebservices.net/ServiceContracts/2008/01">
<soapenv:Header/>
<soapenv:Body>
<ns:GetCitiesByCountry>
<ns:Country>USA</ns:Country>
</ns:GetCitiesByCountry>
</soapenv:Body>
</soapenv:Envelope></body>
</xforms:instance>
<xforms:submission id="searchCitiesByName-submission" class="fr-service"
ref="instance('fr-service-request-instance')"
resource="http://www.restfulwebservices.net/wcf/WeatherForecastService.svc"
method="post"
serialization="application/xml"
mediatype="application/soap+xml; action=GetCitiesByCountry"
replace="instance"
instance="searchCitiesByName-instance"/>
<xforms:action ev:event="xforms-submit" ev:observer="searchCitiesByName-submission">
<xxforms:variable name="request-instance-name" select="'searchCitiesByName-instance'"
as="xs:string"/>
<xforms:insert nodeset="instance('fr-service-request-instance')"
origin="saxon:parse(instance($request-instance-name))"/>
</xforms:action>
</xforms:model>
</xhtml:head>
<xhtml:body>
<fr:view>
<xforms:label ref="instance('fr-form-metadata')/title"/>
<fr:body>
<fr:section id="section-1-section" bind="section-1-bind">
<xforms:label ref="$form-resources/section-1/label"/>
<xforms:help ref="$form-resources/section-1/help"/>
<fr:grid columns="1">
<xhtml:tr>
<xhtml:td>
<fr:autocomplete
id="codeSearch"
bind="codeSearch-bind"
dynamic-itemset="true"
max-results-displayed="50">
<xforms:label ref="$form-resources/codeSearch/label"/>
<!-- React to user searching -->
<xforms:action ev:event="fr-search-changed">
<xxforms:variable name="search-value" select="event('fr-search-value')"/>
<xxforms:variable name="make-suggestion" select="string-length($search-value) >= 2"/>
<xforms:action if="$make-suggestion">
<xforms:setvalue ref="instance('code-search-instance')/searchFor" value="$search-value"/>
<xforms:send submission="searchCitiesByName-submission"/>
</xforms:action>
<xforms:action if="not($make-suggestion)">
<!-- Delete itemset -->
<xforms:delete nodeset="instance('searchCitiesByName-instance')//*:GetCitiesByCountryResult/*:string"/>
</xforms:action>
</xforms:action>
<xforms:itemset nodeset="instance('searchCitiesByName-instance')//*:GetCitiesByCountryResult/*:string">
<xforms:label ref="text()"/>
<xforms:value ref="text()"/>
</xforms:itemset>
</fr:autocomplete>
</xhtml:td>
</xhtml:tr>
<xhtml:tr>
<xhtml:td>
<widget:xforms-instance-inspector xmlns:widget="http://orbeon.org/oxf/xml/widget" id="orbeon-xforms-inspector"/>
</xhtml:td>
</xhtml:tr>
</fr:grid>
</fr:section>
</fr:body>
</fr:view>
</xhtml:body>
</xhtml:html>
Any help is appreciated.