Definition
- org.json – a lightweight library where
XML.toJSONObject(xmlString)converts XML to aJSONObjectandXML.toString(jsonObject)converts back; fast but lossy and type-unsafe. - Jackson XmlMapper – reads XML into a typed Java object, while
ObjectMapperwrites the same object as JSON, giving type-safe, annotation-driven conversion. - @JacksonXmlRootElement – sets the XML root tag name.
- @JacksonXmlProperty(isAttribute = true) – marks a field as an XML attribute rather than a child element.
- Round-trip safety – XML has no native number or boolean type, so without a typed object, numbers can become strings during conversion.
National Bank of India receives payment instructions from two kinds of partners. Old legacy banks send XML with formal opening and closing tags. Modern fintech partners send JSON with braces and colons. The bank's clerk must read either format and rewrite it into the other without losing any information. org.json is a quick pencil translation on a napkin. Jackson is a proper typed form that guarantees amounts stay as numbers and currencies stay as controlled values.
Understanding the Concepts
What is the org.json approach?
The simplest approach uses the org.json library. XML.toJSONObject(xmlString) returns a JSONObject, and XML.toString(jsonObject) converts back. This is fast and needs no Java classes, but it is purely structural.
package com.bank.util;
import org.json.JSONObject;
import org.json.XML;
public class QuickConvert {
public static void main(String[] args) {
String xml = "<payment><id>PAY-001</id><amount>50000</amount>" +
"<currency>INR</currency></payment>";
JSONObject json = XML.toJSONObject(xml);
System.out.println(json.toString(2));
String backToXml = XML.toString(json);
System.out.println(backToXml);
}
}
{
"payment": {
"id": "PAY-001",
"amount": 50000,
"currency": "INR"
}
}
<payment><id>PAY-001</id><amount>50000</amount><currency>INR</currency></payment>
What are the limitations of org.json?
org.json is lossy and ambiguous. It cannot distinguish XML attributes from child elements. A single repeated element might become an object or an array depending on whether there is one or many. And since XML has no native number type, round-tripping can turn a number into a string.
// Ambiguity: one <item> becomes an object, two become an array
String xml1 = "<order><item>Loan</item></order>";
String xml2 = "<order><item>Loan</item><item>Card</item></order>";
System.out.println(XML.toJSONObject(xml1));
// {"order":{"item":"Loan"}} – item is a String
System.out.println(XML.toJSONObject(xml2));
// {"order":{"item":["Loan","Card"]}} – item is an Array!
What is the Jackson approach?
With Jackson you define a typed Java class, read XML with XmlMapper, and write JSON with ObjectMapper. Because both formats go through the same object, the conversion is type-safe.
package com.bank.dto;
import com.fasterxml.jackson.dataformat.xml.annotation.*;
import java.math.BigDecimal;
@JacksonXmlRootElement(localName = "payment")
public class Payment {
@JacksonXmlProperty(isAttribute = true)
private String id;
private BigDecimal amount;
private String currency;
// getters and setters
}
package com.bank.util;
import com.bank.dto.Payment;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class TypedConvert {
public static void main(String[] args) throws Exception {
String xml = "<payment id=\"PAY-001\"><amount>50000</amount>" +
"<currency>INR</currency></payment>";
// XML → Java object
XmlMapper xmlMapper = new XmlMapper();
Payment payment = xmlMapper.readValue(xml, Payment.class);
// Java object → JSON
ObjectMapper jsonMapper = new ObjectMapper();
String json = jsonMapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(payment);
System.out.println(json);
// JSON → Java object → XML
Payment fromJson = jsonMapper.readValue(json, Payment.class);
String backToXml = xmlMapper.writeValueAsString(fromJson);
System.out.println(backToXml);
}
}
{
"id" : "PAY-001",
"amount" : 50000,
"currency" : "INR"
}
<payment id="PAY-001"><amount>50000</amount><currency>INR</currency></payment>
Notice that id was an XML attribute (not a child element), and the typed object preserved this distinction through the round trip.
Why is BigDecimal critical for money fields?
XML has no native number type, so everything is text. Without a typed object, a round trip through XML can turn a JSON number 50000 into a string "50000". With a typed Payment class that declares amount as BigDecimal, the conversion is safe regardless of format.
// Wrong – double loses precision:
private double amount; // 50000.10 might become 50000.09999999...
// Correct – BigDecimal preserves exact value:
private BigDecimal amount; // 50000.10 stays exactly 50000.10
How do you handle XML namespaces?
Legacy banking XML often carries namespaces. Jackson handles namespaces with @JacksonXmlProperty(namespace = "..."). For strict validation, pair conversion with an XSD validator.
@JacksonXmlRootElement(localName = "payment",
namespace = "http://schema.bank.com/payments/v1")
public class Payment {
@JacksonXmlProperty(isAttribute = true)
private String id;
private BigDecimal amount;
private String currency;
}
How do you handle lists in XML?
XML wraps repeated elements differently than JSON arrays. Jackson's @JacksonXmlElementWrapper controls whether a list has a wrapper element.
public class Statement {
@JacksonXmlElementWrapper(localName = "transactions")
@JacksonXmlProperty(localName = "transaction")
private List<Transaction> transactions;
}
// XML with wrapper:
<statement>
<transactions>
<transaction><amount>5000</amount></transaction>
<transaction><amount>3000</amount></transaction>
</transactions>
</statement>
// JSON:
{ "transactions": [ { "amount": 5000 }, { "amount": 3000 } ] }
What are common mistakes?
The most common mistake is using org.json for financial data where type safety matters. The second mistake is using double for money instead of BigDecimal. The third is forgetting that XML attributes require the isAttribute = true annotation in Jackson.
// Wrong – org.json for banking payments (lossy, type-unsafe):
JSONObject json = XML.toJSONObject(partnerXml);
BigDecimal amount = json.getBigDecimal("amount"); // may fail if string
// Correct – Jackson typed conversion:
Payment payment = xmlMapper.readValue(partnerXml, Payment.class);
BigDecimal amount = payment.getAmount(); // always BigDecimal
When should you use each approach?
| Scenario | org.json | Jackson |
|---|---|---|
| Quick admin tool, informal data | Good enough | Overkill |
| Financial payments, strict types | Dangerous | Required |
| Attribute vs element distinction | Lost | Preserved |
| List handling (single vs array) | Ambiguous | Controlled |
| Namespace support | None | Full |
What should a senior engineer know?
In finance, the format is secondary but the types are sacred. Routing every conversion through a typed object guarantees that an amount is always a BigDecimal and a currency is always a controlled value regardless of whether it arrived as JSON or XML. The conversion step and the validation step (XSD for XML, JSON Schema for JSON) are separate concerns. A senior engineer also knows that Jackson's XmlMapper and ObjectMapper share the same annotation model, so one POJO with dual annotations can serve both formats in a gateway that mediates between legacy XML partners and modern JSON clients.
Visual Overview
Conversion Paths: org.json (quick, lossy): XML string ──→ XML.toJSONObject() ──→ JSONObject ──→ XML.toString() ──→ XML string Jackson (typed, safe): XML string ──→ XmlMapper.readValue() ──→ Payment (Java) ──→ ObjectMapper.writeValueAsString() ──→ JSON string JSON string ──→ ObjectMapper.readValue() ──→ Payment (Java) ──→ XmlMapper.writeValueAsString() ──→ XML string ┌──────────────────────────────────────────────────┐ │ Typed Java object (Payment) is the bridge │ │ BigDecimal amount stays exact in both formats │ └──────────────────────────────────────────────────┘
Key Takeaway
To convert between JSON and XML in Java, there are two approaches depending on how much type safety you need. For quick structural conversion with no Java classes, the org.json library provides XML.toJSONObject and XML.toString. This is fast but lossy: it cannot distinguish XML attributes from elements, can be ambiguous about repeated elements, and since XML has no native number type, it can turn a number into a string.
For anything involving money or strict contracts, use Jackson. Define a typed Java class and bind both formats to it: Jackson's XmlMapper reads XML into the object, and ObjectMapper writes it as JSON, and the reverse direction works the same way. Because both formats go through the same typed object, the conversion is safe and controlled by annotations.
At National Bank of India, the payments gateway received instructions in XML from legacy correspondent banks and in JSON from fintech partners. The team modelled the payment as one typed object with the id as an XML attribute and the amount as a BigDecimal. Converting an incoming XML payment to JSON kept the amount as a real number, and converting back to XML restored the attribute, so the round trip was lossless.
The lesson is that in finance the format is secondary but the types are sacred. Routing every conversion through a typed object guarantees that an amount is always a BigDecimal and a currency is always a controlled value, regardless of whether it arrived as JSON or XML.
Remember one sentence: convert through a typed Java object so numbers stay numbers, attributes stay attributes, and real money never loses precision in translation.
---