How to convert json to android xml resource files
- Step 1Shape the JSON like a resources tree — The converter is structural, so model the target XML directly. Use a top-level object whose single key is
string(orstring-array,bool,integer) holding an array, and set the Root element option toresources. - Step 2Add the name attribute with the @ prefix — Every resource element needs a
name. Use the default@attribute prefix:{ "@name": "app_title", "#text": "My App" }. The@namekey becomesname="app_title"and#textbecomes the element body. - Step 3Build string-array entries from item arrays — For a
<string-array>, nest anitemarray:{ "string-array": { "@name": "days", "item": ["Mon","Tue"] } }produces<string-array name="days"><item>Mon</item><item>Tue</item></string-array>. Theitemkey repeats per element. - Step 4Pick a 4-space indent — Choose 4 spaces to match Android Studio's default XML formatting, or 2 spaces if your project's
.editorconfiguses them. Keep XML declaration on so the file starts with<?xml version="1.0" encoding="UTF-8"?>. - Step 5Convert and download as strings.xml — Click Convert to XML, then Download. The file is named
<input>.xml; rename it tostrings.xml(orarrays.xml) and drop it inapp/src/main/res/values/. For locales, put translated versions invalues-fr/,values-de/, etc. - Step 6Build and let lint check it — Run the project.
aapt/ lint flags duplicatenameattributes, illegal characters, and unescaped apostrophes the converter passed through. Review those — the converter does not deduplicate names or apply Android's backslash-escape preference.
JSON shape → Android resource XML
How to model each resource type in JSON so the structural converter emits valid Android XML. Root element option set to resources in every row.
| Resource type | JSON to feed the converter | Resulting XML element |
|---|---|---|
| Single string | { "string": { "@name": "ok", "#text": "OK" } } | <string name="ok">OK</string> |
| Many strings | { "string": [ {"@name":"a","#text":"A"}, {"@name":"b","#text":"B"} ] } | Two <string> elements with distinct names |
| String array | { "string-array": { "@name":"days", "item":["Mon","Tue"] } } | <string-array name="days"><item>Mon</item><item>Tue</item></string-array> |
| Boolean | { "bool": { "@name":"enabled", "#text":"true" } } | <bool name="enabled">true</bool> |
| Integer | { "integer": { "@name":"max", "#text":"5" } } | <integer name="max">5</integer> |
Escaping: what aapt needs vs what the converter does
The converter applies XML entity escaping. Android resource files have an extra layer of rules on top of XML; review these after conversion.
| Character in value | Converter output | Android note |
|---|---|---|
& (ampersand) | & | Correct and required — fixes aapt 'unterminated entity reference' |
< / > | < / > | Valid XML; safe in resource text |
' (apostrophe) | ' | Valid XML and compiles, but Android docs prefer \' — both work; lint may suggest the backslash form |
" (double quote) | " (in attribute and text) | Valid; Android prefers \" inside string text but accepts the entity |
@ at start of value text | passed through literally | A leading @ in a string value is fine; a leading @ in a key becomes an attribute |
Cookbook
Real JSON inputs and the exact Android resource XML the converter produces. The trick is always: root = resources, attributes via @, text via #text.
A handful of strings into strings.xml
ExampleModel each string as an object with @name and #text, grouped under a string array. Set the root to resources.
Input JSON:
{ "string": [
{ "@name": "app_title", "#text": "My App" },
{ "@name": "action_ok", "#text": "OK" }
] }
Options: Root element = resources, indent = 4
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="app_title">My App</string>
<string name="action_ok">OK</string>
</resources>A string-array for a spinner
ExampleNest an item array under a string-array object. Each element of the array becomes one <item>.
Input JSON:
{ "string-array": {
"@name": "weekdays",
"item": ["Monday", "Tuesday", "Wednesday"]
} }
Options: Root element = resources
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string-array name="weekdays">
<item>Monday</item>
<item>Tuesday</item>
<item>Wednesday</item>
</string-array>
</resources>Ampersand and apostrophe get escaped
ExampleValues with & or ' are entity-escaped, which is what stops the classic aapt compile failures. Review the apostrophe form if lint nudges you toward backslashes.
Input JSON:
{ "string": { "@name": "terms", "#text": "Read T&Cs — it's free" } }
Options: Root element = resources
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="terms">Read T&Cs — it's free</string>
</resources>A plurals block, modeled by hand
ExampleThere is no plurals helper. Model the <plurals> structure as nested JSON: an item array whose entries carry a @quantity attribute and #text.
Input JSON:
{ "plurals": {
"@name": "songs",
"item": [
{ "@quantity": "one", "#text": "%d song" },
{ "@quantity": "other", "#text": "%d songs" }
]
} }
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<plurals name="songs">
<item quantity="one">%d song</item>
<item quantity="other">%d songs</item>
</plurals>
</resources>What NOT to expect: nested objects are NOT flattened
ExampleA nested design-token object does not become underscore-joined names. It becomes nested elements, which is not valid resource XML. Flatten the JSON first if you want button_ok style keys.
Input JSON:
{ "button": { "ok": "OK", "cancel": "Cancel" } }
Output XML (NOT a valid resources file):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<button>
<ok>OK</ok>
<cancel>Cancel</cancel>
</button>
</root>
Fix: flatten with json-flattener to button_ok / button_cancel,
then reshape to { "string": [ {"@name":"button_ok",...} ] }.Errors 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.
You expected an 'Android XML' output mode
By designThere is no output-format dropdown and no Android mode. The tool is a generic structural JSON→XML mapper. You get the <resources> shape by setting the Root element to resources and modeling string entries with @name / #text, as shown above — not by selecting a preset.
Nested JSON objects were not flattened with underscores
Not a resources fileNested objects become nested child elements, not button_ok-style flat keys. Android resource files are flat (one element per resource), so a nested tree won't compile. Run the JSON through json-flattener first (it uses dotted or underscore-style keys), then reshape into the string array form.
Duplicate name attributes
aapt errorThe converter does not deduplicate name values. If two entries share @name of app_title, you get two <string name="app_title"> elements and aapt fails with a duplicate-resource error at build time. Dedupe the keys in your JSON before converting (for example with json-key-filter or a manual review).
Apostrophe rendered as ' instead of \'
Compiles, lint nuanceThe converter entity-escapes ' to ', which is valid XML and aapt accepts it. Android's documentation prefers the \' backslash form inside string text. Both compile; if your team enforces the backslash style via lint, do a find/replace on ' → \' after download.
Resource name is not a legal identifier
aapt errorAndroid resource names must be valid Java identifiers (letters, digits, underscores; not starting with a digit). The converter writes whatever you put in @name verbatim, so @name of 2nd-button produces name="2nd-button" and the build fails. Sanitize names to second_button style before converting.
Invalid JSON pasted
Parse errorInput is parsed with JSON.parse; a trailing comma or unquoted key throws and nothing converts. Many design-token exports are technically JSON5 — repair them with json-format-fixer first.
Empty array yields a missing element
OmittedAn empty item array ("item": []) produces no <item> children and, if it's the only child, an empty parent. An empty string-array is allowed in Android, but verify the element still appears — empty arrays are dropped from the output entirely.
File exceeds the free size limit
Blocked (Pro)JSON to XML is a Pro tool. Free inputs are capped at 2 MB (more than enough for a normal strings.xml); Pro raises it to 100 MB for very large localized token sets. Processing is in-browser regardless of tier.
Frequently asked questions
How do I get the <resources> wrapper?
Set the Root element option to resources. The whole JSON object is wrapped in <resources>...</resources>. There is no Android-specific mode — resources is just the root name you supply, the same way you'd type root for a generic document.
How does a string resource get its name attribute?
Use the @ attribute prefix. Model each string as { "@name": "key", "#text": "value" }. The @name key becomes name="key" and #text becomes the element body, giving <string name="key">value</string>. Without @name, the element has no name attribute and R.string.key won't resolve.
Does the tool auto-detect string arrays from JSON arrays?
No. A JSON array doesn't automatically become <string-array>. You model it: { "string-array": { "@name": "days", "item": ["Mon","Tue"] } }. The item key repeats per array element, producing the <item> children that getStringArray() reads.
Are nested JSON objects flattened to underscore-joined keys?
No — that's a common misconception. Nested objects become nested XML elements, which is not a valid resources file. Flatten the JSON first with json-flattener, then reshape the flat keys into the string array form before converting.
How are apostrophes and ampersands handled?
They're entity-escaped: & → &, ' → ', " → ", plus < and >. This fixes the most common aapt compile errors (unterminated entity, unescaped apostrophe). Android's docs prefer \' and \" inside string text; the entity forms also compile, so convert them after download only if your lint config requires it.
How do I do quantity strings (plurals)?
Model the <plurals> element by hand: a plurals object with @name and an item array whose entries carry @quantity and #text. See the plurals example in the cookbook. There's no dedicated plurals helper because the converter is purely structural.
Can I generate localized values-xx files in one pass?
Not in a single conversion — the tool outputs one XML document per run. Convert each locale's JSON separately and save them to values-fr/, values-de/, etc. Keep the same @name keys across locales so R.string references resolve in every locale.
Will my unreleased UI copy be uploaded anywhere?
No. Conversion is entirely client-side. Proprietary copy, internal config values, and unreleased feature strings never reach JAD Apps servers. Only an anonymous run counter (no content) is recorded for signed-in dashboard stats.
What indent should I use to match Android Studio?
Android Studio's default XML formatter uses 4 spaces, so pick 4 spaces. If your project's .editorconfig overrides that to 2, pick 2. You can also pick Minified for a single-line file, though that's unusual for committed resource files.
Why did my element name come out malformed?
The converter writes JSON keys as element names verbatim — it does not sanitize. A key with a space, hyphen at the start, or leading digit produces an illegal element name. Keep your structural keys (string, string-array, item, plurals) exactly as Android expects, and put dynamic names in the @name attribute, not in the element name.
How big a config can I convert?
JSON to XML is Pro. Free tier caps the input at 2 MB; Pro at 100 MB. A typical strings.xml source is well under 1 MB, so free tier is usually fine; very large multi-locale token dumps may need Pro.
How do I reverse this — XML resources back to JSON?
Use xml-to-json to parse a strings.xml back into JSON. Its removeNSPrefix option strips namespace prefixes from element names if your XML uses any. From there you can re-flatten or re-key with json-key-renamer.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.