xml-js icon indicating copy to clipboard operation
xml-js copied to clipboard

remove _text

Open Nickx58 opened this issue 6 years ago • 10 comments

"title": { "_text": "You Won't Believe What These 31 Oscar Winners Look Like Now" },

Nickx58 avatar Feb 27 '18 07:02 Nickx58

Try adding a textFn callback

convert.xml2json(xmlMsg,{textFn:RemoveJsonTextAttribute});

function RemoveJsonTextAttribute(value,parentElement){ try{ var keyNo = Object.keys(parentElement._parent).length; var keyName = Object.keys(parentElement._parent)[keyNo-1]; parentElement._parent[keyName] = value; } catch(e){} }

gpender avatar Apr 20 '18 09:04 gpender

It is not completely right: it does not works for not string values

if I have this xml:

<person>
    <age>18</age>
    <name>Mario</name>
    <diplomated>false</diplomated>
<person>

and this options:

var options = {
    compact: true,
    trim: true,
    nativeType: true,
    ignoreDeclaration: true,
    ignoreInstruction: true,
    ignoreAttributes: true,
    ignoreComment: true,
    ignoreCdata: true,
    ignoreDoctype: true,
    textFn: removeJsonTextAttribute
  };

with nativeType set at true values like "true" or "12" will be true and 12, but in the lib this happens before the calling of textFn, wich is made by the external module sax. So textFn will manage only elements with string value if nativeType is set at true.

The result would be:

{
 person: {
  age: { _text: 18; },
  name: "mario",
  diplomated: { _text: false; }
 }
}

if native type would have been false the result would be:

{
 person: {
  age: "18",
  name: "mario",
  diplomated: "false"
 }
}

THE SOLUTION if you want both nativeType: true and _text removed I found this solution:

function nativeType(value) {
    var nValue = Number(value);
    if (!isNaN(nValue)) {
      return nValue;
    }
    var bValue = value.toLowerCase();
    if (bValue === 'true') {
      return true;
    } else if (bValue === 'false') {
      return false;
    }
    return value;
  }

var removeJsonTextAttribute = function(value, parentElement) {
    try {
      var keyNo = Object.keys(parentElement._parent).length;
      var keyName = Object.keys(parentElement._parent)[keyNo - 1];
      parentElement._parent[keyName] = nativeType(value);
    } catch (e) {}
  }

var options = {
    compact: true,
    trim: true,
    ignoreDeclaration: true,
    ignoreInstruction: true,
    ignoreAttributes: true,
    ignoreComment: true,
    ignoreCdata: true,
    ignoreDoctype: true,
    textFn: removeJsonTextAttribute
  };

//Then use the xml2js function or xml2json function

explained: I copied the function that convert string in its native type from the lib, I copied the remove text function from gpender but I added the nativeType function at the value. I removed the nativeType: true option. So, because nativeType is false, the not string types won't be ignored by the textFn functions and _text will be removed for them too. The textFn function will convert the string to its native type so the result is:

{
 person: {
  age: 18,
  name: "mario",
  diplomated: false
 }
}

euberdeveloper avatar May 16 '18 17:05 euberdeveloper

Good call!

gpender avatar May 16 '18 17:05 gpender

hello, some issues with this method, <Dossier><Objectif>obj1</Objectif><Objectif>obj2</Objectif><Objectif>obj3</Objectif></Dossier> will return {Objectif: 'obj3' } i think parentElement._parent[keyName] = nativeType(value) erase the last value

mickadoua avatar Jul 25 '18 22:07 mickadoua

@mickadoua

this snippet solve this problem

const removeJsonTextAttribute = function(value, parentElement) {
  try {
    const parentOfParent = parentElement._parent;
    const pOpKeys = Object.keys(parentElement._parent);
    const keyNo = pOpKeys.length;
    const keyName = pOpKeys[keyNo - 1];
    const arrOfKey = parentElement._parent[keyName];
    const arrOfKeyLen = arrOfKey.length;
    if (arrOfKeyLen > 0) {
      const arr = arrOfKey;
      const arrIndex = arrOfKey.length - 1;
      arr[arrIndex] = value;
    } else {
      parentElement._parent[keyName] = value;
    }
  } catch (e) {}
};

egoarka avatar Mar 18 '19 23:03 egoarka

Is there a way to get this to completely remove tags like <example/> where there is never anything in the object? I don't care about reproducing the XML as I'm just converting a third party XML API into JSON so it is easier to work with. The API sends empty tags when there is no information for that datapoint but that leaves my JSON full of those keys like example: {} but I'd rather have example: null or just not have it at all.

JamesCoyle avatar May 03 '19 13:05 JamesCoyle

@mickadoua

this snippet solve this problem

const removeJsonTextAttribute = function(value, parentElement) {
  try {
    const parentOfParent = parentElement._parent;
    const pOpKeys = Object.keys(parentElement._parent);
    const keyNo = pOpKeys.length;
    const keyName = pOpKeys[keyNo - 1];
    const arrOfKey = parentElement._parent[keyName];
    const arrOfKeyLen = arrOfKey.length;
    if (arrOfKeyLen > 0) {
      const arr = arrOfKey;
      const arrIndex = arrOfKey.length - 1;
      arr[arrIndex] = value;
    } else {
      parentElement._parent[keyName] = value;
    }
  } catch (e) {}
};

This does not work in combination with nativeType: true

paul-uz avatar May 20 '22 10:05 paul-uz

Yes, removeJsonTextAttribute function does not work when nativeType: true. Is there any solution?

quyendqmz avatar Oct 10 '22 08:10 quyendqmz

Yes, removeJsonTextAttribute function does not work when nativeType: true. Is there any solution?

Set nativeType in options to false and let the nativeType function handle converting the values

var options = {
  compact: true,
  nativeType: false,
  textFn: removeJsonTextAttribute
};

const nativeType = function(value) {
  let nValue = Number(value);
  if (!isNaN(nValue)) {
    return nValue;
  }
  let bValue = value.toLowerCase();
  if (bValue === 'true') {
    return true; 
  } else if (bValue === 'false') {
    return false;
  }
  return value;
}

const removeJsonTextAttribute = function(value, parentElement) {
  try {
    const parentOfParent = parentElement._parent;
    const pOpKeys = Object.keys(parentElement._parent);
    const keyNo = pOpKeys.length;
    const keyName = pOpKeys[keyNo - 1];
    const arrOfKey = parentElement._parent[keyName];
    const arrOfKeyLen = arrOfKey.length;
    if (arrOfKeyLen > 0) {
      const arr = arrOfKey;
      const arrIndex = arrOfKey.length - 1;
      arr[arrIndex] = value;
    } else {
      parentElement._parent[keyName] = nativeType(value);
    }
  } catch (e) {}
};

moldypenguins avatar Oct 26 '22 02:10 moldypenguins

Is there a way to get this to completely remove tags like <example/> where there is never anything in the object? I don't care about reproducing the XML as I'm just converting a third party XML API into JSON so it is easier to work with. The API sends empty tags when there is no information for that datapoint but that leaves my JSON full of those keys like example: {} but I'd rather have example: null or just not have it at all.


I found the solution to solve the 'example: {} ' object problem, by directly modifying the library. For this, I went to node_modules, "xml-js" > lib > xmljs.js and on line 286, there is the "onEndElement" method. Here, add the following code:

if (typeof currentElement[name] === 'object' && Object.keys(currentElement[name]).length === 0) { currentElement[name] = options.defaultValue

I define that when a property comes with an empty object, I change the value of said property. In order to dynamically define the value to change to the options that are passed to them, I add a property called 'defaultValue' and pass the value in question

codigo options node_modules

JCLedesmaDev avatar Nov 04 '22 14:11 JCLedesmaDev