Jeg tror, du kan undgå problemet ved at erstatte tom tekst med en midlertidig værdi, opdatere al den anden tekst og derefter erstatte den midlertidige værdi med en null.
Jeg forstår ikke XPath, der er sikkert en meget bedre måde at gøre dette på, men det ser ud til at virke:
SELECT
--#3: Replace the temporary value with null, this keeps the start and end tag
UpdateXML(
--#2: Replace everything but the temporary value
UpdateXML(
--#1: Replace empty text with a temporary value
UpdateXML(xmlData, '/TEST/VALUE[not(text())]', '<VALUE>TEMPORARY VALUE</VALUE>')
,'/TEST/VALUE[text()!="TEMPORARY VALUE"]/text()', 'hello')
,'/TEST/VALUE[text()="TEMPORARY VALUE"]/text()', null) examle
FROM (SELECT XMLType('<TEST><VALUE>hi</VALUE><VALUE>hola</VALUE><VALUE></VALUE></TEST>') as xmlData FROM DUAL);