Data Sanitizer • Zero Dependencies

tally-clean-response

Lightweight, zero-dependency utility to clean, sanitize, and normalize JSON responses from Tally ERP 9 / TallyPrime XML-to-JSON parsers into clean, developer-friendly JavaScript objects.

0 Dependencies
Node.js >= 18
TypeScript Ready
ESM Native

Why is this needed? #

When you query Tally via XML/TDL (using libraries like tally-to-xml-tdl or fast-xml-parser), the parsed JSON contains heavy XML serialization artifacts:

  • Typed wrappers: Scalar values are wrapped in type metadata objects, e.g. {"@_TYPE": "String", "#text": "Pur Exp"} or {"@_TYPE": "Number", "#text": 1} instead of plain values.
  • Empty tag noise: Empty XML tags like <ALLINVENTORYENTRIES.LIST TYPE="String"/> become {"@_TYPE": "String"} instead of clean empty values.
  • Deep nesting: Collection records are buried inside ENVELOPE.BODY.DATA.COLLECTION.*.
  • Inconsistent single-item collections: Tally returns a single object instead of an array when only 1 record matches, causing random .map is not a function runtime bugs.

tally-clean-response solves all of this automatically in a single call, returning a normalized array of pristine JavaScript objects with full recursive unwrapping of all nested inventory and ledger lines.

Before vs After Comparison #

❌ Raw Parsed Tally XML Output (Before)

Every field is wrapped in #text and @_TYPE objects, empty nodes are dirty objects, and single rows are not arrays:

json • Raw Tally Output
{
  "ENVELOPE": {
    "BODY": {
      "DATA": {
        "COLLECTION": {
          "VOUCHER": [
            {
              "DATE": { "@_TYPE": "Date", "#text": "20260401" },
              "GUID": "56b158ca-11af-4ad5-bc28-c1c2428041b9-0002a874",
              "VOUCHERTYPENAME": { "@_TYPE": "String", "#text": "Pur Exp" },
              "VOUCHERNUMBER": { "@_TYPE": "Number", "#text": 1 },
              "ISDEEMEDPOSITIVE": { "@_TYPE": "Logical", "#text": "No" },
              "ALLINVENTORYENTRIES.LIST": { "@_TYPE": "String" },
              "@_REMOTEID": "56b158ca-11af-4ad5-bc28-c1c2428041b9-0002a874"
            }
          ]
        }
      }
    }
  }
}

✅ Cleaned Output (After cleanTallyResponse)

A direct, clean JavaScript array with pure primitives, normalized empty strings, and preserved metadata:

json • Cleaned Output
[
  {
    "DATE": "20260401",
    "GUID": "56b158ca-11af-4ad5-bc28-c1c2428041b9-0002a874",
    "VOUCHERTYPENAME": "Pur Exp",
    "VOUCHERNUMBER": 1,
    "ISDEEMEDPOSITIVE": "No",
    "ALLINVENTORYENTRIES.LIST": "",
    "@_REMOTEID": "56b158ca-11af-4ad5-bc28-c1c2428041b9-0002a874"
  }
]

Installation #

bash
npm install tally-clean-response

Quick start #

1. Together with tally-to-xml-tdl

The standard way to fetch and immediately sanitize Tally data in Node.js:

javascript
import { vouchers } from "tally-to-xml-tdl";
import cleanTallyResponse from "tally-clean-response";

// 1. Fetch raw response from Tally Prime
const rawData = await vouchers.purchases.period("My Company", "1-Apr-2026", "30-Apr-2026");

// 2. Clean into a pristine array of vouchers
const cleanVouchers = cleanTallyResponse(rawData);

console.log(`Cleaned ${cleanVouchers.length} vouchers:`);
console.log(cleanVouchers[0]);

2. Usage with fast-xml-parser

If you query Tally Prime using raw HTTP and fast-xml-parser:

javascript
import { XMLParser } from "fast-xml-parser";
import cleanTallyResponse from "tally-clean-response";

const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: "@_",
  textNodeName: "#text"
});

const rawJson = parser.parse(tallyXmlResponseString);
const cleanedRecords = cleanTallyResponse(rawJson);

Type Conversion Matrix #

When unwrapping leaf nodes with @_TYPE:

Tally @_TYPE Sample Raw Input Cleaned Output Result Type
String {"@_TYPE": "String", "#text": "Sales"} "Sales" string
Date {"@_TYPE": "Date", "#text": "20260401"} "20260401" string
Logical {"@_TYPE": "Logical", "#text": "Yes"} "Yes" string
Rate {"@_TYPE": "Rate", "#text": "500/Nos"} "500/Nos" string
Number {"@_TYPE": "Number", "#text": 125} 125 number
Amount {"@_TYPE": "Amount", "#text": 15000.5} 15000.5 number / string
Quantity {"@_TYPE": "Quantity", "#text": 10} 10 number / string
Empty tag {"@_TYPE": "String"} (no #text) "" string

Attributes prefixed with @_ (such as @_REMOTEID, @_VCHKEY, @_VCHTYPE) attached directly to row objects are preserved intact for indexing and joins.

TypeScript Support #

Full TypeScript definitions are included out of the box with generic type parameter support:

typescript
import cleanTallyResponse from "tally-clean-response";

interface VoucherRow {
  DATE: string;
  GUID: string;
  VOUCHERTYPENAME: string;
  VOUCHERNUMBER: number;
  [key: string]: any;
}

const cleaned = cleanTallyResponse<VoucherRow>(tallyRawResponse);
// cleaned has type VoucherRow[]