virDomainDeviceDefParse(const char *xmlStr,
                        const virDomainDef *def,
                        virDomainXMLOptionPtr xmlopt,
                        void *parseOpaque,
                        unsigned int flags)
{
    g_autoptr(xmlDoc) xml = NULL;
    xmlNodePtr node;
    g_autoptr(xmlXPathContext) ctxt = NULL;
    g_autoptr(virDomainDeviceDef) dev = NULL;

    if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt)))
        return NULL;

    node = ctxt->node;

    if (VIR_ALLOC(dev) < 0)
        return NULL;

    if ((dev->type = virDomainDeviceTypeFromString((const char *) node->name)) < 0) {
        /* Some crazy mapping of serial, parallel, console and channel to
         * VIR_DOMAIN_DEVICE_CHR. */
        if (virXMLNodeNameEqual(node, "channel") ||
            virXMLNodeNameEqual(node, "console") ||
            virXMLNodeNameEqual(node, "parallel") ||
            virXMLNodeNameEqual(node, "serial")) {
            dev->type = VIR_DOMAIN_DEVICE_CHR;
        } else {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown device type '%s'"),
                           node->name);
            return NULL;
        }
    }

    switch ((virDomainDeviceType) dev->type) {
    case VIR_DOMAIN_DEVICE_DISK:
        if (!(dev->data.disk = virDomainDiskDefParseXML(xmlopt, node, ctxt,
                                                        flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_LEASE:
        if (!(dev->data.lease = virDomainLeaseDefParseXML(node)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_FS:
        if (!(dev->data.fs = virDomainFSDefParseXML(xmlopt, node, ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_NET:
        if (!(dev->data.net = virDomainNetDefParseXML(xmlopt, node, ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_INPUT:
        if (!(dev->data.input = virDomainInputDefParseXML(xmlopt, def, node,
                                                          ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_SOUND:
        if (!(dev->data.sound = virDomainSoundDefParseXML(xmlopt, node,
                                                          ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_WATCHDOG:
        if (!(dev->data.watchdog = virDomainWatchdogDefParseXML(xmlopt,
                                                                node, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_VIDEO:
        if (!(dev->data.video = virDomainVideoDefParseXML(xmlopt, node,
                                                          ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_HOSTDEV:
        if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, node,
                                                              ctxt,
                                                              flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_CONTROLLER:
        if (!(dev->data.controller = virDomainControllerDefParseXML(xmlopt, node,
                                                                    ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_GRAPHICS:
        if (!(dev->data.graphics = virDomainGraphicsDefParseXML(xmlopt, node,
                                                                ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_HUB:
        if (!(dev->data.hub = virDomainHubDefParseXML(xmlopt, node, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_REDIRDEV:
        if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(xmlopt, node,
                                                                ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_RNG:
        if (!(dev->data.rng = virDomainRNGDefParseXML(xmlopt, node,
                                                      ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_CHR:
        if (!(dev->data.chr = virDomainChrDefParseXML(xmlopt,
                                                      ctxt,
                                                      node,
                                                      flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_SMARTCARD:
        if (!(dev->data.smartcard = virDomainSmartcardDefParseXML(xmlopt, node,
                                                                  ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_MEMBALLOON:
        if (!(dev->data.memballoon = virDomainMemballoonDefParseXML(xmlopt,
                                                                    node,
                                                                    ctxt,
                                                                    flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_NVRAM:
        if (!(dev->data.nvram = virDomainNVRAMDefParseXML(xmlopt, node, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_SHMEM:
        if (!(dev->data.shmem = virDomainShmemDefParseXML(xmlopt, node,
                                                          ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_TPM:
        if (!(dev->data.tpm = virDomainTPMDefParseXML(xmlopt, node, ctxt, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_PANIC:
        if (!(dev->data.panic = virDomainPanicDefParseXML(xmlopt, node, flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_MEMORY:
        if (!(dev->data.memory = virDomainMemoryDefParseXML(xmlopt, node,
                                                            ctxt, def,
                                                            flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_IOMMU:
        if (!(dev->data.iommu = virDomainIOMMUDefParseXML(node, ctxt)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_VSOCK:
        if (!(dev->data.vsock = virDomainVsockDefParseXML(xmlopt, node, ctxt,
                                                          flags)))
            return NULL;
        break;
    case VIR_DOMAIN_DEVICE_NONE:
    case VIR_DOMAIN_DEVICE_LAST:
        break;
    }

    /* callback to fill driver specific device aspects */
    if (virDomainDeviceDefPostParseOne(dev, def, flags,
                                       xmlopt, parseOpaque) < 0)
        return NULL;

    /* validate the configuration */
    if (virDomainDeviceDefValidate(dev, def, flags, xmlopt) < 0)
        return NULL;

    return g_steal_pointer(&dev);
}