jsonix icon indicating copy to clipboard operation
jsonix copied to clipboard

Null properties are dropped during marshalling/unmarshalling with JSONIX

Open erotavlas opened this issue 8 years ago • 0 comments

I noticed that in my conversion from JSON to XML, if one of my JSON objects is null, the node for that property is absent from the XML, for example

JSON

{
	"name": {
		"namespaceURI": "",
		"localPart": "myApp",
		"prefix": "",
		"key": "myApp",
		"string": "myApp"
	},
	"value": {
		"TYPE_NAME": "generated.MyApp",
		"inputData": [{
			"TYPE_NAME": "generated.MyApp.InputData",
			"dataType": "text",
			"mandatory": false,
			"length": 25,
			"valid": true,
                        "value":null
		}]
	}
}

Convert to XML output (marshalString)

        var context2 = new j.Context([generated2]);
        var marshaller2 = context2.createMarshaller();
        var objectAsXMLString2 = marshaller2.marshalString(template2);
<myApp>
   <inputData>
      <dataType>text</dataType>
      <mandatory>false</mandatory>
      <length>25</length>
      <valid>true</valid>
   </inputData>
</myApp>

Then converting back again to JSON (unmarshalString)

        var unmarshaller2 = context2.createUnmarshaller();
        var resultJSON2 = unmarshaller2.unmarshalString(objectAsXMLString2 );
{
	"name": {
		"namespaceURI": "",
		"localPart": "myApp",
		"prefix": "",
		"key": "myApp",
		"string": "myApp"
	},
	"value": {
		"TYPE_NAME": "generated.MyApp",
		"inputData": [{
			"TYPE_NAME": "generated.MyApp.InputData",
			"dataType": "text",
			"mandatory": false,
			"length": 25,
			"valid": true
		}]
	}
}

So as you see, there is loss of information. When the complete cycle of conversion occurs, JSON -> XML -> back to JSON, some properties are not preserved. Anything that is null gets dropped - in this case it is the property 'value'. This is a problem for downstream applications that are expecting certain properties to be present on an object.

My XML schema that was used to generate the jsonix mapping object looks like this

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="myApp">
    <xs:complexType>
      <xs:sequence>
		<xs:element name="inputData" maxOccurs="unbounded" minOccurs="0">
          <xs:complexType>
            <xs:sequence >
              <xs:element type="xs:string" name="dataType"/>
              <xs:element type="xs:boolean" name="mandatory"/>
              <xs:element type="xs:short" name="length"/>
              <xs:element type="xs:string" name="elementId"/>
              <xs:element type="xs:string" name="formId"/>
              <xs:element type="xs:anySimpleType" name="value" />
              <xs:element type="xs:boolean" name="valid"/>
              <xs:element type="xs:short" name="precision"/>
              <xs:element type="xs:short" name="scale"/>
            </xs:sequence>
          </xs:complexType>
		  </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

And generated mapping using jsonix-schema-compiler is

var generated_Module_Factory = function () {
  var generated = {
    name: 'generated',
    typeInfos: [{
        localName: 'MyApp',
        typeName: null,
        propertyInfos: [{
            name: 'inputData',
            minOccurs: 0,
            collection: true,
            elementName: {
              localPart: 'inputData'
            },
            typeInfo: '.MyApp.InputData'
          }]
      }, {
        localName: 'MyApp.InputData',
        typeName: null,
        propertyInfos: [{
            name: 'dataType',
            required: true,
            elementName: {
              localPart: 'dataType'
            }
          }, {
            name: 'mandatory',
            required: true,
            elementName: {
              localPart: 'mandatory'
            },
            typeInfo: 'Boolean'
          }, {
            name: 'length',
            required: true,
            elementName: {
              localPart: 'length'
            },
            typeInfo: 'Short'
          }, {
            name: 'elementId',
            required: true,
            elementName: {
              localPart: 'elementId'
            }
          }, {
            name: 'formId',
            required: true,
            elementName: {
              localPart: 'formId'
            }
          }, {
            name: 'value',
            required: true,
            elementName: {
              localPart: 'value'
            },
            typeInfo: 'AnySimpleType'
          }, {
            name: 'valid',
            required: true,
            elementName: {
              localPart: 'valid'
            },
            typeInfo: 'Boolean'
          }, {
            name: 'precision',
            required: true,
            elementName: {
              localPart: 'precision'
            },
            typeInfo: 'Short'
          }, {
            name: 'scale',
            required: true,
            elementName: {
              localPart: 'scale'
            },
            typeInfo: 'Short'
          }]
      }],
    elementInfos: [{
        typeInfo: '.MyApp',
        elementName: {
          localPart: 'myApp'
        }
      }]
  };
  return {
    generated: generated
  };
};
if (typeof define === 'function' && define.amd) {
  define([], generated_Module_Factory);
}
else {
  var generated_Module = generated_Module_Factory();
  if (typeof module !== 'undefined' && module.exports) {
    module.exports.generated = generated_Module.generated;
  }
  else {
    var generated = generated_Module.generated;
  }
}

Is there a way to preserve all properties during the conversion, even if they are null?

erotavlas avatar Aug 17 '17 15:08 erotavlas