Try this to understand the implicit defaults:
SELECT CAST(NULL AS INT)
,CAST('' AS INT)
,CAST(' ' AS INT);
A NULL remains unchanged, but empty is defaulted to a zero. That is the designed behaviour.
Now back to your issue:
DECLARE @DietaryTypeXML XML='<dietTypes>
<dietType>
<dietaryID></dietaryID> <!-- This is empty -->
</dietType>
</dietTypes>';
--The engine gets an empty value and defaults it to a zero.
--If you use varchar instead, you get a blank, but you'll have to deal with string-typed numbers.
SELECT DietTypes.Col.value('dietaryID[1]', 'INT') AS Empty_in_INT_is_implicitly_taken_as_zero
,DietTypes.Col.value('dietaryID[1]', 'VARCHAR(100)') AS Empty_in_VARCHAR_is_implicitly_taken_as_empty_string
FROM @DietaryTypeXML.nodes('//dietType') DietTypes(Col);
--The solution I suggest:
SELECT DietTypes.Col.value('dietaryID[1] cast as xs:int?', 'INT')
FROM @DietaryTypeXML.nodes('//dietType') DietTypes(Col);
Using the XQuery cast as xs:int? tells the engine, that the given value is a nullable int. Now the value returned is no more empty but NULL.