Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wsdl option ignoreAttributesUndefined #904

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
2024-11-11, Version 4.1.6
=========================

Placeholder for package maintainer

* feat: Override default strong-soap behaviour when mapping undefined element values to self closing tags (<tag/> instead of <tag xsi:nil=true></tag>. Option is ignoreAttributesUndefined, to enable option set to false. (Jamie Nicholson)

Comment on lines +1 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to manually update this file.

It is auto-generated by strong-tools as part of our publishing workflow.

2024-10-10, Version 4.1.5
=========================

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,10 @@ soap.createClient(__dirname + '/wsdl/default_namespace.wsdl', wsdlOptions, funct

To see it in practice, consider the sample in: [test/request-response-samples/addPets__force_namespaces](https://github.com/loopbackio/strong-soap/tree/master/test/request-response-samples/addPets__force_namespaces)

### Mapping of undefined element values

When the wsdl attribute nillable="true" it set, JSON values will map to null and XML values map to nillable='true' unless ignoreAttributesUndefined is set to false. When ignoreAttributesUndefined is set to false undefined element values will map to a self closing tag i.e. <tag/>

Comment on lines +533 to +536
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reword this?

Suggested change
### Mapping of undefined element values
When the wsdl attribute nillable="true" it set, JSON values will map to null and XML values map to nillable='true' unless ignoreAttributesUndefined is set to false. When ignoreAttributesUndefined is set to false undefined element values will map to a self closing tag i.e. <tag/>
### Mapping of JSON `undefined` values
For XML elements that are marked as `nillable` in their descriptor, JSON `undefined` is mapped to `<tag xsi:nil="true" />` by default.
Setting `jsonUndefinedAsXmlNil: false` will cause JSON `undefined` to be mapped to a `<tag/>`.

## XMLHandler

XMLHandler enables you to to convert a JSON object to XML and XML to a JSON object. It can also parse an XML string or stream into the XMLBuilder tree.
Expand Down
6 changes: 4 additions & 2 deletions src/parser/xmlHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class XMLHandler {
* @param {Object} [options.date]
* @param {Object} [options.date.timezone]
* @param {boolean} [options.date.timezone.enabled]
* @param {boolean} [option.ignoreAttributesUndefined]
*/
constructor(schemas, options) {
this.schemas = schemas || {};
Expand All @@ -43,6 +44,7 @@ class XMLHandler {
this.options.date = this.options.date || {};
this.options.date.timezone = this.options.date.timezone || {};
this.options.date.timezone.enabled = typeof this.options.date.timezone.enabled === 'boolean' ? this.options.date.timezone.enabled : true;
this.options.ignoreAttributesUndefined = typeof this.options.ignoreAttributesUndefined === 'boolean' ? this.options.ignoreAttributesUndefined : true;
}

jsonToXml(node, nsContext, descriptor, val) {
Expand Down Expand Up @@ -162,8 +164,8 @@ class XMLHandler {
}
}

if (val == null) {
if (descriptor.isNillable) {
if (val === null ||(val === undefined && this.options.ignoreAttributesUndefined == true)) {
if (descriptor.isNillable ) {
Comment on lines +167 to +168
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though about this for a while; The naming is a little unintuitive.

My suggestion is jsonUndefinedAsXmlNil:

  1. It informs the flow of the conversion (JSON->XML)
  2. It clearly states what it does (Coerce JSON undefined as XML xsi:nil)

The only thing missing is stating that the XML element must be defined as nillable, but that can be covered by documentation.

Suggested change
if (val === null ||(val === undefined && this.options.ignoreAttributesUndefined == true)) {
if (descriptor.isNillable ) {
if (val === null ||(val === undefined && this.options.jsonUndefinedAsXmlNil == true)) {
if (descriptor.isNillable) {

// Set xsi:nil = true
declareNamespace(nsContext, element, 'xsi', helper.namespaces.xsi);
if (typeof element.attribute === 'function') {
Expand Down
99 changes: 99 additions & 0 deletions test/undefined-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright IBM Corp. 2017. All Rights Reserved.
// Copyright LoopBack contributors 2024

// Node module: strong-soap
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

"use strict";

var soap = require('..').soap,
assert = require('assert');

describe('Undefined tests', function() {

/*
If value of 'breed' (simpleType in the WSDL) is undefined,
and options.ignoreAttributesUndefined is true, the element will be empty.

Request
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<ns1:addPets xmlns:ns1="http://tempuri.org/">
<pet>
<Name>max</Name>
<Breed/>
</pet>
<pet>
<Name>sunny</Name>
<Breed>Labrador</Breed>
</pet>
</ns1:addPets>
</soap:Body>
</soap:Envelope>
*/

it("undefined and options.ignoreAttributesUndefined false for simpleType", function (done) {
soap.createClient(__dirname + '/wsdl/strict/nillable.wsdl',{
"ignoreAttributesUndefined":false
}, function (err, client) {
assert.ok(!err);
var requestArgs = {
"pet": [
{
"Name" : "max",
"Breed" : undefined
},
{
"Name": "sunny",
"Breed": "Labrador"
}
]
};

client.addPets(requestArgs, function (err, result, body) {
var request = client.lastMessage;
//check if the Breed element is empty
var index = request.indexOf('<Breed/>');
assert.ok(index > -1);
done();
});
});
});

/*
In case of nillable="true" defined on 'pet' complexType in the WSDL. If value of 'pet' is undefined,
and options.ignoreAttributesUndefined is true the element will be empty.

Request
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<ns1:addPets xmlns:ns1="http://tempuri.org/">
<pet/>
</ns1:addPets>
</soap:Body>
</soap:Envelope>
*/

it("undefined and options.ignoreAttributesUndefined false for complexType", function (done) {
soap.createClient(__dirname + '/wsdl/strict/nillable.wsdl',{
"ignoreAttributesUndefined":false
}, function (err, client) {
assert.ok(!err);
var requestArgs = {
"pet": undefined
};

client.addPets(requestArgs, function (err, result, body) {
var request = client.lastMessage;
//check if the pet element has xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true" atttribute
var index = request.indexOf('<pet/>');
assert.ok(index > -1);
done();
});
});
});

});