Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parsing XML with custom deserializers #124

Open
jbrooksuk opened this issue Apr 12, 2017 · 9 comments
Open

Parsing XML with custom deserializers #124

jbrooksuk opened this issue Apr 12, 2017 · 9 comments
Labels

Comments

@jbrooksuk
Copy link

Hey! I've recently found this package and it looks like it'll solve all of my needs, the custom deserializers look perfect for what I'm trying to achieve, I'm just having some difficulty implementing with non-namespaced XML...

I'm trying to parse the following:

<?xml version="1.0"?>
<response method="createsession" success="N">
  <errors>
    <error code="0" text="Bad details">
  </errors>
  <request>
    <auth password="test" username="test" />
    <method action="createsession" sitename="site.com" currency="GBP" status="Test" />
  </request>
</response>

What I expect I'd end up with is a custom Response reader that contains:

  • Attributes for the method and success values
  • An Errors reader that'll be array of Error classes which have mapped the code and text attributes
  • And finally a Request reader which does the same

How can I achieve this? Every time I try to read the result, I either exceed the memory usages or my Response reader won't continue to parse the errors or request elements.

Thanks for your help!

@staabm
Copy link
Member

staabm commented Apr 12, 2017

did you check the docs? http://sabre.io/xml/reading/

how does your code look like atm?

@jbrooksuk
Copy link
Author

@staabm yeah, I did, but the docs all seem to be expecting namespaces (which this response doesn't contain).

Here is what I have so far:

$service = new Service();
$service->elementMap['response'] = Response::class;
// $service->elementMap['errors'] = Errors::class;
// $service->elementMap['error'] = Error::class;

$ns = 'response';
$body = $service->parse((string) $response->getBody(), null, $ns); // Where $response is the response from Guzzle

Then my Response@xmlDeserialize is:

public static function xmlDeserialize(Reader $reader)
{
    $response = new self();

    if ($method = $reader->getAttribute('method')) {
        $response->method = $method;
    }

    if ($success = $reader->getAttribute('success')) {
        $response->success = strtoupper($success) === 'Y';
    }

    return $response;
}

Nothing else gets parsed from there onwards though.

@staabm
Copy link
Member

staabm commented Apr 12, 2017

how does the Response class look like?

for non namespaced classes your clark notation just needs to be like {}Response

@jbrooksuk
Copy link
Author

jbrooksuk commented Apr 12, 2017

class Response implements XmlDeserializable
{
    /**
     * The method.
     *
     * @var string
     */
    public $method;

    /**
     * Whether the response was successful.
     *
     * @var string
     */
    public $success;

    /**
     * The errors.
     *
     * @var \Errors
     */
    public $errors;

    /**
     * {@inheritdoc}
     */
    public static function xmlDeserialize(Reader $reader)
    {
        $response = new self();

        if ($method = $reader->getAttribute('method')) {
            $response->method = $method;
        }

        if ($success = $reader->getAttribute('success')) {
            $response->success = strtoupper($success) === 'Y';
        }

        $reader->parseInnerTree();

        return $response;
    }
}

@staabm
Copy link
Member

staabm commented Apr 12, 2017

on the first look I can only see 1 mistake (curly braces missing in your element map):

$service = new Service();
$service->elementMap['{}response'] = Response::class;

@jbrooksuk
Copy link
Author

Those curly braces didn't seem to do anything, so I left them off.

This is what I have now:

$service = new Service();
$service->elementMap = [
    '{}response'    => 'Sabre\Xml\Deserializer\keyValue',
    '{}request'     => Request::class,
    '{}errors'      => Errors::class,
    '{}error'       => Error::class,
];

$ns = 'response';
$body = $service->parse((string) $response->getBody(), null, $ns);

Which gives me:

array:2 [
  "{}errors" => BlueBayTravel\Fusion\Readers\Errors {#834
    +errors: array:1 [
      0 => BlueBayTravel\Fusion\Readers\Error {#847
        +code: null
        +text: "Invalid username and/or password."
      }
    ]
  }
  "{}request" => BlueBayTravel\Fusion\Readers\Request {#839
    +auth: null
    +method: null
  }
]

@jbrooksuk
Copy link
Author

So I've managed to get errors to work, just not the request yet.

@jbrooksuk
Copy link
Author

Oh and I'd still want to wrap the entire response element within the Response class and map from there.

@staabm
Copy link
Member

staabm commented Apr 12, 2017

Those curly braces didn't seem to do anything, so I left them off.

they represent the xmlnamespace. {} means no-namespace.

So I've managed to get errors to work, just not the request yet.

how is the Request class looking?

Oh and I'd still want to wrap the entire response element within the Response class and map from there.

$reader->parseInnerTree(); will return the "child-elements" so you can use whatever it returns per class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants