How to convert xml to json for rest api migration
- Step 1Capture a representative SOAP response — Use Charles Proxy, Fiddler, mitmproxy, or
curl -d @request.xml -H 'Content-Type: text/xml' <endpoint>to capture a real response from the legacy service. Save the full XML — envelope, headers, and body. Capture a response with two-or-more repeated records, not just one (see the singleton edge case below). - Step 2Open the XML to JSON tool — This is a Pro tool. Drop the captured
.xmlfile onto the dropzone above. Files up to 2 MB run on the free tier for evaluation; signed-in Pro raises the per-file limit to 100 MB, enough for verbose multi-record SOAP dumps. - Step 3Enable Strip namespaces — Tick Strip namespaces to drop
soap:,wsse:, and vendor prefixes from tag and attribute names. Note this also removes thexmlns:*declaration attributes themselves, so the output is clean prefix-free JSON — exactly what you want for designing camelCase REST fields. - Step 4Decide on attributes and type coercion — Leave Parse attributes on if SOAP fault codes, currency units, or
xsi:typehints matter to the contract; turn it off to drop all attributes and keep only element text. Leave Coerce types on to convert numbers/booleans, or off if you have IDs with leading zeros (see edge cases) that must stay strings. - Step 5Convert and read the data tree — Click Convert to JSON. The output panel shows the JSON with your chosen indent (Minified, 2, or 4 spaces). Trace down
Envelope.Body.<Operation>Responseto find the domain object — that nested path is your REST resource boundary. - Step 6Design the REST contract from the shape — Copy the JSON and base your OpenAPI schema on it. Plan to: unwrap the envelope (do this in your adapter — the tool keeps it), rename
@-keyed attributes, fix any singleton fields that should always be arrays, and re-key fields to camelCase. Use json-to-typescript to draft interfaces or json-schema-generator for the JSON Schema.
What each option does to a SOAP payload
The four real controls in the tool, mapped to SOAP-migration outcomes. attributePrefix (@) and textNodeName (#text) are fixed defaults — they are not exposed in the UI.
| Option (UI label) | Effect on SOAP XML | Default |
|---|---|---|
| Strip namespaces | <soap:Body>→Body, <wsse:Token>→Token; also removes the xmlns:* declaration attributes. Caution: two prefixed siblings that differ only by prefix can collide to the same key | Off |
| Parse attributes | On: <amt currency="USD">99</amt>→{"#text":99,"@currency":"USD"}. Off: attributes dropped entirely, element keeps only its text/children | On |
| Coerce types | On: <qty>5</qty>→5, <ok>true</ok>→true. Off: every value stays a string. Applies to both element text and attribute values | On |
| Indent (Minified / 2 / 4) | Output formatting only — JSON.stringify spacing. Minified = single line, no payload-shape effect | 2 spaces |
SOAP structures and how they land in JSON
Common SOAP/WSDL response constructs and their faithful JSON shape. Note what the tool does NOT collapse for you.
| SOAP construct | JSON result | Migration note |
|---|---|---|
<soap:Envelope><soap:Body><Op>...</Op></soap:Body></soap:Envelope> | {Envelope:{Body:{Op:{...}}}} (prefixes shown stripped) | Envelope is NOT unwrapped — strip it in your adapter; the tool preserves the full path |
Two <Item> siblings | Item: [ {...}, {...} ] — an array | Good — repeated children become arrays automatically |
One <Item> sibling | Item: {...} — an object, NOT an array | Trap: capture multi-record responses, or force-array in your client code |
<fault><faultcode>soap:Server</faultcode></fault> | {fault:{faultcode:"soap:Server"}} (text value, prefix kept inside the string) | Strip namespaces affects TAG names, not text content — fault strings keep their prefix |
<![CDATA[<receipt>..</receipt>]]> | Single plain string "<receipt>..</receipt>" | Embedded XML is delivered as a string — re-run it through the tool to parse the inner document |
Cookbook
Real SOAP-shaped fragments and the exact JSON the converter produces. Endpoints and tokens anonymised.
Stripping the SOAP envelope clutter
ExampleStrip namespaces removes the prefixes that make a SOAP body hard to read. The envelope nesting stays — you unwrap that in your adapter — but the names are now REST-friendly.
Input (captured SOAP body):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetOrderResponse>
<Order id="4471"><Total>129.50</Total></Order>
</GetOrderResponse>
</soap:Body>
</soap:Envelope>
Options: Strip namespaces ON, Parse attributes ON, Coerce types ON
Output:
{
"Envelope": {
"Body": {
"GetOrderResponse": {
"Order": { "@id": 4471, "Total": 129.5 }
}
}
}
}The singleton-vs-array trap that breaks REST clients
Examplefast-xml-parser collapses repeated siblings into an array, but a single occurrence stays a scalar/object. If your test capture had one line item and production has many, your client code throws on the array. Always capture a multi-record response.
One <Line> (test capture):
<Invoice><Line>A</Line></Invoice>
=> { "Invoice": { "Line": "A" } } <-- string
Two <Line> (production):
<Invoice><Line>A</Line><Line>B</Line></Invoice>
=> { "Invoice": { "Line": ["A", "B"] } } <-- array
Migration fix: in the REST adapter, always coerce Line to an
array: const lines = [].concat(invoice.Line ?? []);Attributes as data vs. transport metadata
ExampleSOAP uses attributes for both real data (currency, units) and infrastructure (xsi:type, soap:mustUnderstand). Parse attributes keeps them as @-keys so you can triage; turn it off to drop all of them.
Input:
<Payment amount="40.00" currency="EUR" xsi:type="CardPayment"/>
Parse attributes ON:
{ "Payment": { "@amount": 40, "@currency": "EUR", "@xsi:type": "CardPayment" } }
Parse attributes OFF:
{ "Payment": "" } <-- attributes gone; empty element => ""
Keep ON, then in the contract: @amount -> amount (number),
@currency -> currency (enum), drop @xsi:type (transport hint).CDATA carrying an embedded XML receipt
ExampleLegacy SOAP services often stuff a second XML document inside a CDATA block. The tool unwraps CDATA to a plain string in one pass — then you run that string back through the tool to parse the inner document.
Input:
<Resp><Receipt><![CDATA[<r><id>9</id><ok>true</ok></r>]]></Receipt></Resp>
Pass 1 output:
{ "Resp": { "Receipt": "<r><id>9</id><ok>true</ok></r>" } }
Pass 2 (paste the Receipt string back in):
{ "r": { "id": 9, "ok": true } }From JSON tree to typed REST contract
ExampleOnce the shape is clear, hand the JSON to a sibling tool to generate the typed artefacts your REST service needs — no manual transcription of field names.
Workflow:
1. xml-to-json (Strip namespaces ON) -> order.json
2. /tool/json-to-typescript -> Order interface for the Node adapter
3. /tool/json-schema-generator -> JSON Schema for OpenAPI components
4. /tool/json-path-extractor -> pull Envelope.Body.*Response to
drop the envelope wrapper in one expressionErrors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
The SOAP envelope is NOT unwrapped automatically
By designThere is no 'Unwrap SOAP envelope' toggle in this tool. The converter faithfully preserves structure, so you always get Envelope.Body.<Operation>Response.<data>. Drop the envelope in your adapter, or use json-path-extractor with an expression like $.Envelope.Body to slice it off after conversion.
Single child element does not become an array
Singleton trapA response with one <Item> yields Item: {...} (object); two-or-more yield Item: [...] (array). The parser is configured with no forced-array paths, so it cannot know a field is 'always a collection'. Capture a multi-record response, or normalise to an array in your REST client. This is the single most common cause of x.map is not a function after a SOAP→REST cutover.
Stripped namespace prefixes can collide
Key collisionWith Strip namespaces on, <ns1:Id> and <ns2:Id> both become Id. If they are siblings, fast-xml-parser merges them into an array on the Id key — silently mixing two semantically different fields. If prefixes carry meaning in your payload, leave Strip namespaces off and rename in your mapping layer instead.
Malformed SOAP does not raise an error
Silent parsefast-xml-parser is lenient: a mismatched tag like <a></b> parses without throwing, producing a structurally wrong object rather than an error. Validate your captured XML in a real XML validator first if the source endpoint is flaky — the tool will not flag the corruption for you.
Leading-zero IDs lose the zeros under Coerce types
Data loss<accountId>007</accountId> becomes the number 7 when Coerce types is on — the leading zeros are gone. SOAP services often use zero-padded reference numbers. Turn Coerce types OFF to keep every value as a string, then selectively cast only the fields you know are numeric in your contract.
Very large integer IDs survive as strings
PreservedA 20-digit transaction ID exceeds JavaScript's safe-integer range. fast-xml-parser detects this and keeps it as a string even with Coerce types on, preventing the precision loss you would get from blind Number(). So <txn>10000000000000000001</txn> stays "10000000000000000001" — correct behaviour for REST contracts.
Mixed-content text loses inter-element spacing
Spacing lossAn element with text interleaved with child tags — <p>See <b>note</b> below</p> — concatenates the text fragments into #text: "Seebelow" with the space dropped. SOAP rarely uses mixed content, but vendor 'description' fields sometimes do. If you hit this, treat the element as opaque HTML at the source rather than relying on the parsed #text.
wsse security headers are parsed, not removed
Review<wsse:Security> headers (UsernameToken, Timestamp, signatures) are parsed into the JSON like any other element. They are not data you want in a REST contract. Strip them in your adapter or extract only the body with json-key-filter. Because this runs in-browser, the credentials never reach a server — but they are still in the JSON output you copy.
Pro tool: free tier caps evaluation files at 2 MB
Plan limitXML to JSON is a Pro tool. You can convert files up to 2 MB on the free tier to evaluate it; signed-in Pro raises the per-file limit to 100 MB. A multi-thousand-record SOAP dump easily exceeds 2 MB, so use a single representative response for design work or upgrade for full batch conversion.
Frequently asked questions
Does the tool unwrap the SOAP envelope for me?
No. There is no SOAP-unwrap option — the converter preserves the full document structure, so you get Envelope.Body.<Operation>Response. Drop the envelope in your REST adapter, or run json-path-extractor with $.Envelope.Body after conversion to slice it off in one expression.
How are XML attributes handled in the JSON output?
With Parse attributes on (the default), each attribute becomes an @-prefixed key on its element object — <item id="1">x</item> → {"#text": "x", "@id": 1}. The prefix is fixed at @ (not configurable in the UI). Turn the option off to drop all attributes entirely. Element text alongside attributes is stored under the #text key.
How do repeated XML elements appear — always as arrays?
Repeated sibling elements with the same tag name are collected into a JSON array; a single occurrence stays a scalar or object, NOT a one-element array. The tool does not force any path to be always-array. Test with responses containing two or more records, or normalise to an array in your client, to avoid the classic singleton crash on REST cutover.
What does Strip namespaces actually remove?
It removes namespace prefixes from element and attribute names (soap:Body→Body, @wsse:Type→@Type) and drops the xmlns:* declaration attributes. It does NOT alter text content — a fault string like soap:Server inside an element value keeps its prefix because that is data, not a tag name.
Can I rename fields to camelCase during conversion?
No — the tool does not rename keys. It maps XML tag names verbatim (minus prefixes if you strip namespaces). Do the camelCase remap after conversion with json-key-renamer, or in your adapter when you build the REST response object.
How should I handle CDATA blocks carrying embedded XML?
CDATA content is unwrapped to a plain string in one pass. If that string is itself an XML document (common in legacy SOAP), copy it and paste it back into the tool for a second pass to parse the inner structure. CDATA carrying HTML is delivered as an HTML string you can store as-is.
Will type coercion break my zero-padded reference numbers?
Yes, if Coerce types is on: 007→7. Turn Coerce types OFF to keep every value a string, then cast only the genuinely numeric fields in your contract. Note that very large integers that would lose precision are kept as strings even when coercion is on, which is the safe behaviour for IDs.
Is the SOAP response data uploaded anywhere?
No. Parsing runs entirely in your browser via fast-xml-parser. Production payloads with customer records, payment data, or wsse:UsernameToken credentials never reach JAD Apps servers. Only an anonymous run counter (no content) is recorded for signed-in dashboard stats.
Does it validate the XML or report errors?
No. fast-xml-parser is lenient and will parse mildly malformed XML (e.g. mismatched tags) without throwing, producing a wrong-but-not-flagged object. Validate captured payloads in a strict XML validator first if the legacy endpoint is unreliable.
Can I convert a WSDL file the same way?
Yes — a WSDL is XML, so it converts structurally. But a WSDL describes operations and types, not data; the JSON is verbose and namespace-heavy. For contract design you usually want a sample RESPONSE converted to JSON, then json-schema-generator, rather than the WSDL itself.
How big a SOAP response can I convert?
This is a Pro tool. Free-tier evaluation caps each file at 2 MB; signed-in Pro raises the per-file limit to 100 MB. For SOAP→REST design you only need one representative response, so the free limit is usually enough to inspect the shape.
What is the recommended end-to-end migration workflow?
Capture a multi-record response → convert here with Strip namespaces on → json-path-extractor to drop the envelope → json-to-typescript for adapter types → json-schema-generator for the OpenAPI components. Each step is browser-local, so the payload never leaves your machine.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.