Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zend Xml2Json Laravel Package

zendframework/zend-xml2json

Converts XML to JSON using PHP's DOM and JSON extensions, letting you transform XML documents or strings into JSON output ready for APIs, storage, or JavaScript consumption.

Deep Wiki
Context7

Getting Started

zendframework/zend-xml2json is a tiny adapter around simplexml_load_string() plus a JSON encoder. It exposes essentially one useful entry point:

use Zend\Xml2Json\Xml2Json;

$json = Xml2Json::fromXml($xmlString);          // attributes preserved
$json = Xml2Json::fromXml($xmlString, true);    // attributes ignored

Minimal setup:

composer require zendframework/zend-xml2json

Requirements: PHP with ext-simplexml (and ext-libxml) enabled. There is no service provider, no config file, and no facade — it is a stateless static helper, so it drops into a Laravel service or controller without wiring.

Where to look first:

  • src/Xml2Json.php for the public API and error behavior.
  • The package tests for the exact JSON shape produced for attributes, text, repeated siblings, and empty elements. The output format is the contract, not the class API.
  • Composer's abandonment notice: the package is archived and superseded by laminas/laminas-xml2json (which is itself worth checking for current maintenance status before adopting).

First realistic use case in a Laravel app: accepting a legacy XML payload (SOAP-ish partner feed, payment callback, shipping manifest) at an endpoint and returning JSON to a modern frontend or downstream API.

Route::post('/webhooks/legacy', function (Request $request) {
    $json = Xml2Json::fromXml($request->getContent());

    return response($json, 200, ['Content-Type' => 'application/json']);
});

Note the response is returned as raw JSON, not response()->json(json_decode(...)), to avoid decoding and re-encoding the payload.

Implementation Patterns

Thin service wrapper. Hide the static call behind a small class so your app can swap the implementation later (important given the package is archived):

final class XmlToJsonConverter
{
    public function convert(string $xml, bool $ignoreAttributes = false): array
    {
        return json_decode(
            Xml2Json::fromXml($xml, $ignoreAttributes),
            true,
            512,
            JSON_THROW_ON_ERROR
        );
    }
}

Normalize the output shape before it hits your domain. The raw structure uses @attributes and @text conventions and is unpleasant to consume. Add a recursive normalizer that flattens @attributes into prefixed keys and unwraps @text:

private function flatten(array $node): array
{
    $out = [];
    foreach ($node as $key => $value) {
        if ($key === '@attributes' && is_array($value)) {
            foreach ($value as $attr => $v) {
                $out['attr_' . $attr] = $v;
            }
        } elseif ($key === '@text') {
            $out['value'] = $value;
        } elseif (is_array($value)) {
            $out[$key] = $this->flatten($value);
        } else {
            $out[$key] = $value;
        }
    }
    return $out;
}

Middleware for XML request bodies. Laravel does not parse XML into the request bag. Add a middleware that converts XML bodies once and merges the result, so controllers work with $request->input(...):

if (str_contains($request->header('Content-Type', ''), 'xml')) {
    $converted = $converter->convert($request->getContent());
    $request->merge($converted);
}

Only do this on routes that expect XML — mutating every request is a debugging trap.

Queue large documents. Conversion is in-memory and synchronous. For multi-megabyte feeds, accept the upload, store it on a disk, dispatch a job that reads the file and converts it, then persist the JSON to a json column or dispatch a further job. Never do this inline in a web request.

Testing. Keep XML fixtures under tests/Fixtures/*.xml and assert against decoded arrays, not raw JSON strings (key ordering and whitespace make string assertions brittle). For feature tests, send XML with the explicit call helper rather than postJson:

$this->call('POST', '/webhooks/legacy', [], [], [], [
    'CONTENT_TYPE' => 'application/xml',
], $xml);

Integration tip for existing Laminas/ZF code. If you are pulling this in because a legacy service already uses it, isolate it in one adapter class. The class namespace changes (Zend\Xml2Json → Laminas\Xml2Json) and the rest of your Laravel app should not care.

Gotchas and Tips

  • Abandoned and unmaintained. Composer will print an abandonment warning; treat it as a hard signal. On PHP 8.x the package's composer.json constraints (PHP 5.6/7.x era) may block installation entirely. Prefer laminas/laminas-xml2json, or replace it with ~10 lines of simplexml_load_string() + recursive array conversion, which also lets you control the output shape.
  • Unstable shapes for repeated elements. A single <item> becomes an object; two <item> elements become an array. Your consuming code must normalize to arrays before iterating, or you will get intermittent "trying to access array offset on string" errors in production.
  • Lossy content. Mixed content (text interleaved with child elements), comments, processing instructions, and namespaces are not faithfully represented. Namespace prefixes and xmlns can appear as ordinary keys/attributes. Do not use this to round-trip XML.
  • Everything is a string. Numbers, booleans, and nulls come back as strings. Cast explicitly after conversion; do not rely on the JSON encoder to infer types.
  • Attribute conventions collide conceptually. @attributes and @text are reserved keys. If your downstream flattening logic is naive, it will clash with real element names or produce inconsistent field names. Keep the normalization step centralized.
  • Error handling is limited. Invalid XML surfaces as a RuntimeException (and libxml warnings) rather than a rich parse error. Wrap conversion in a try/catch, validate content length and content type first, and return a 422 for malformed payloads.
  • XXE and entity safety. The package gives you no knobs over libxml entity handling. If you process untrusted XML, do not feed arbitrary documents to it blindly — validate, cap size, and consider a custom conversion using libxml_set_external_entity_loader() / LIBXML_NONET so you control entity resolution.
  • Empty and whitespace elements are ambiguous. <foo/>, <foo></foo>, and <foo> </foo> may not produce the representation you expect. Write a fixture test for each form you actually receive before building logic on top.
  • No extension points. There is no config, no plugin system, and the API is static. If you need different conventions, do not subclass — write your own converter and keep the package behind an interface so removal later is a one-file change.
  • Debugging tip. When output looks wrong, first dump simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA) and inspect the tree. The bug is almost always in SimpleXML's representation, not in the JSON encoding step.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource