На этот вопрос ответили во многих вариациях, но ни один из них не относится к моей ситуации.
Я извлекаю данные с помощью WSMan, который затем возвращает вывод в виде sudo-xml. Я бы даже не посчитал это «настоящим» xml, так как в нем так много нестандартных атрибутов. Проблема в том, что мне нужно иметь возможность ссылаться на вывод как объект в PHP. Так что на данный момент я использую много str_replace. Проблема в том, что если нестандартный формат отклоняется (в некоторых случаях он возвращает что-то вроде этого <KeyID xsi:nil="true"/>
в других случаях это может быть что-то вроде этого <CMCIP xsi:nil="true"/>
), трудно предвидеть все различные атрибуты, которые мне придется учитывать и извлекать из переменной, прежде чем импортировать ее как объект, используя simplexml_load_string.
Итак, мой вопрос во всей простоте: есть ли способ загрузить нестандартный XML в объект? Вот пример данных xml, чтобы вы знали, с каким безумием мы имеем дело здесь.
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration">
<s:Header>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/09/enumeration/EnumerateResponse</wsa:Action>
<wsa:RelatesTo>uuid:3ae2d181-04f0-14f0-8002-89040b5d1500</wsa:RelatesTo>
<wsa:MessageID>uuid:43a291ab-04f0-14f0-8073-b516f1d9bed4</wsa:MessageID>
</s:Header>
<s:Body>
<wsen:EnumerateResponse>
<wsen:EnumerationContext>439c90e9-04f0-14f0-8072-b516f1d9bed4</wsen:EnumerationContext>
</wsen:EnumerateResponse>
</s:Body>
</s:Envelope>
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsen="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:n1="http://schemas.dell.com/wbem/wscim/1/cim-schema/2/DCIM_SystemView" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Header>
<wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsa:Action>http://schemas.xmlsoap.org/ws/2004/09/enumeration/PullResponse</wsa:Action>
<wsa:RelatesTo>uuid:3af0a1eb-04f0-14f0-8003-89040b5d1500</wsa:RelatesTo>
<wsa:MessageID>uuid:43a41fe8-04f0-14f0-8074-b516f1d9bed4</wsa:MessageID>
</s:Header>
<s:Body>
<wsen:PullResponse>
<wsen:Items>
<n1:DCIM_SystemView>
<n1:AssetTag/>
<n1:BIOSReleaseDate>11/20/2013</n1:BIOSReleaseDate>
<n1:BIOSVersionString>2.1.3</n1:BIOSVersionString>
<n1:BaseBoardChassisSlot>NA</n1:BaseBoardChassisSlot>
<n1:BatteryRollupStatus>1</n1:BatteryRollupStatus>
<n1:BladeGeometry>255</n1:BladeGeometry>
<n1:BoardPartNumber>061P35A00</n1:BoardPartNumber>
<n1:BoardSerialNumber>CN70163231007K</n1:BoardSerialNumber>
<n1:CMCIP xsi:nil="true"/>
<n1:CPLDVersion>1.0.3</n1:CPLDVersion>
<n1:CPURollupStatus>1</n1:CPURollupStatus>
<n1:ChassisModel/>
<n1:ChassisName>Main System Chassis</n1:ChassisName>
<n1:ChassisServiceTag>REMOVED</n1:ChassisServiceTag>
<n1:ChassisSystemHeight>2</n1:ChassisSystemHeight>
<n1:DeviceDescription>System</n1:DeviceDescription>
<n1:ExpressServiceCode>33088672189</n1:ExpressServiceCode>
<n1:FQDD>System.Embedded.1</n1:FQDD>
<n1:FanRollupStatus>1</n1:FanRollupStatus>
<n1:HostName/>
<n1:InstanceID>System.Embedded.1</n1:InstanceID>
<n1:LastSystemInventoryTime>20140928010936.000000+000</n1:LastSystemInventoryTime>
<n1:LastUpdateTime>20140220171215.000000+000</n1:LastUpdateTime>
<n1:LicensingRollupStatus>1</n1:LicensingRollupStatus>
<n1:LifecycleControllerVersion>2.1.0</n1:LifecycleControllerVersion>
<n1:Manufacturer>Dell Inc.</n1:Manufacturer>
<n1:MaxCPUSockets>2</n1:MaxCPUSockets>
<n1:MaxDIMMSlots>24</n1:MaxDIMMSlots>
<n1:MaxPCIeSlots>6</n1:MaxPCIeSlots>
<n1:MemoryOperationMode>OptimizerMode</n1:MemoryOperationMode>
<n1:Model>PowerEdge R720xd</n1:Model>
<n1:NodeID>F7852V1</n1:NodeID>
<n1:PSRollupStatus>1</n1:PSRollupStatus>
<n1:PlatformGUID>3156324f-c0c6-3580-3810-00374c4c4544</n1:PlatformGUID>
<n1:PopulatedCPUSockets>2</n1:PopulatedCPUSockets>
<n1:PopulatedDIMMSlots>8</n1:PopulatedDIMMSlots>
<n1:PopulatedPCIeSlots>2</n1:PopulatedPCIeSlots>
<n1:PowerCap>598</n1:PowerCap>
<n1:PowerCapEnabledState>3</n1:PowerCapEnabledState>
<n1:PowerState>2</n1:PowerState>
<n1:PrimaryStatus>1</n1:PrimaryStatus>
<n1:RollupStatus>1</n1:RollupStatus>
<n1:ServerAllocation xsi:nil="true"/>
<n1:ServiceTag>REMOVED</n1:ServiceTag>
<n1:StorageRollupStatus>1</n1:StorageRollupStatus>
<n1:SysMemErrorMethodology>6</n1:SysMemErrorMethodology>
<n1:SysMemFailOverState>NotInUse</n1:SysMemFailOverState>
<n1:SysMemLocation>3</n1:SysMemLocation>
<n1:SysMemMaxCapacitySize>1572864</n1:SysMemMaxCapacitySize>
<n1:SysMemPrimaryStatus>1</n1:SysMemPrimaryStatus>
<n1:SysMemTotalSize>65536</n1:SysMemTotalSize>
<n1:SystemGeneration>12G Monolithic</n1:SystemGeneration>
<n1:SystemID>1320</n1:SystemID>
<n1:SystemRevision>0</n1:SystemRevision>
<n1:TempRollupStatus>1</n1:TempRollupStatus>
<n1:UUID>4c4c4544-0037-3810-8035-c6c04f325631</n1:UUID>
<n1:VoltRollupStatus>1</n1:VoltRollupStatus>
<n1:smbiosGUID>44454c4c-3700-1038-8035-c6c04f325631</n1:smbiosGUID>
</n1:DCIM_SystemView>
</wsen:Items>
<wsen:EndOfSequence/>
</wsen:PullResponse>
</s:Body>
</s:Envelope>
То, что вы получите как ответ / вывод, — это объединение нескольких документов XML. В вашем примере это два.
Это не правильный XML, но он также не редкость.
Поэтому все, что вам нужно сделать, это разделить документы и выбрать тот, с которым вам нужно иметь дело (в вашем примере второй):
$split = preg_split('~\Q<?xml version="1.0" encoding="UTF-8"?>\E\R~u', $sequenced_xml, 2, PREG_SPLIT_NO_EMPTY);
$xml = simplexml_load_string($split[1]);
Теперь, когда у вас есть интересующий вас XML-документ, вы можете делать то, что предлагают многие другие ответы о том, как анализировать SOAP-ответ. Больше не существует искаженного XML (который на самом деле представлял собой последовательность сцепленных хорошо сформированных XML-документов).
Остальное связано с пространствами имен.
Некоторые указатели:
… чтобы получить тело конверта SOAP:
$soap = 'http://www.w3.org/2003/05/soap-envelope';
$body = $xml->children($soap)->Body;
… все перечисленные элементы в виде массива:
$wsen = 'http://schemas.xmlsoap.org/ws/2004/09/enumeration';
$xml->registerXPathNamespace('wsen', $wsen);
$items = $body->xpath('.//wsen:*/*[not(namespace-uri(.) = namespace-uri(..))]');
и так далее и тому подобное.
заметка о вашем собственном примере кода, который вы дали в своем ответе: если вы ищете имя элемента в любом пространстве имен, вы можете сделать это в xpath с помощью local-name()
:
$pullitem = 'ServiceTag';
$try = $xml->xpath(sprintf("//*[local-name(.)='%s']", $pullitem));
printf("pullitem '%s' has been foun in the following namespaces:\n", $pullitem);
foreach ($try as $element) {
$nsURI = dom_import_simplexml($element)->namespaceURI;
printf(" - %s\n", $nsURI);
}
это избавит вас от пяти или около того отдельных вызовов xpath.
и если вы, наконец, не хотите заботиться о пространстве имен, так как ожидаете, что оно в любом случае будет «тем одним» каждого элемента, вы можете с помощью DOMDocument создать элемент SimpleXMLElement для каждого результата, извлекая каждое из них в новый документ его своя:
/**
* create a new SimpleXMLElement out of an existing one
*
* @param SimpleXMLElement $item
*
* @return SimpleXMLElement
*/
function simplexml_export_element(SimpleXMLElement $item) {
$doc = new DOMDocument();
$node = $doc->importNode(dom_import_simplexml($item), true);
$node = $doc->appendChild($node);
return simplexml_load_string($doc->saveXML($doc->documentElement), get_class($item), 0, $node->namespaceURI);
}
Такая вспомогательная процедура полезна, так как она помещает собственное пространство имен элемента в качестве пространства имен для нового SimpleXMLElement. Это позволяет напрямую обращаться к детям в одном и том же пространстве имен. Код, использующий его в дальнейшем, не должен заботиться об этом пространстве имен по умолчанию.
Пример:
$split = preg_split('~\Q<?xml version="1.0" encoding="UTF-8"?>\E\R~u', $sequenced_xml, 2, PREG_SPLIT_NO_EMPTY);
$xml = new SimpleXMLElement($split[1]);
$soap = 'http://www.w3.org/2003/05/soap-envelope';
$body = $xml->children($soap)->Body;
$wsen = 'http://schemas.xmlsoap.org/ws/2004/09/enumeration';
$xml->registerXPathNamespace('wsen', $wsen);
$enumerated = $body->xpath('.//wsen:*/*[not(namespace-uri(.) = namespace-uri(..))]');
$enumerated = array_map('simplexml_export_element', $enumerated);
foreach ($enumerated as $item) {
echo $item->getName(), "\n";
foreach ($item as $key => $value) {
printf(" - %s: %s\n", $key, $value);
}
}
Выход:
DCIM_SystemView
- AssetTag:
- BIOSReleaseDate: 11/20/2013
- BIOSVersionString: 2.1.3
- BaseBoardChassisSlot: NA
- BatteryRollupStatus: 1
- BladeGeometry: 255
- BoardPartNumber: 061P35A00
- BoardSerialNumber: CN70163231007K
- CMCIP:
- CPLDVersion: 1.0.3
- CPURollupStatus: 1
- ChassisModel:
- ChassisName: Main System Chassis
- ChassisServiceTag: REMOVED
- ChassisSystemHeight: 2
- DeviceDescription: System
- ExpressServiceCode: 33088672189
- FQDD: System.Embedded.1
- FanRollupStatus: 1
- HostName:
- InstanceID: System.Embedded.1
- LastSystemInventoryTime: 20140928010936.000000+000
- LastUpdateTime: 20140220171215.000000+000
- LicensingRollupStatus: 1
- LifecycleControllerVersion: 2.1.0
- Manufacturer: Dell Inc.
- MaxCPUSockets: 2
- MaxDIMMSlots: 24
- MaxPCIeSlots: 6
- MemoryOperationMode: OptimizerMode
- Model: PowerEdge R720xd
- NodeID: F7852V1
- PSRollupStatus: 1
- PlatformGUID: 3156324f-c0c6-3580-3810-00374c4c4544
- PopulatedCPUSockets: 2
- PopulatedDIMMSlots: 8
- PopulatedPCIeSlots: 2
- PowerCap: 598
- PowerCapEnabledState: 3
- PowerState: 2
- PrimaryStatus: 1
- RollupStatus: 1
- ServerAllocation:
- ServiceTag: REMOVED
- StorageRollupStatus: 1
- SysMemErrorMethodology: 6
- SysMemFailOverState: NotInUse
- SysMemLocation: 3
- SysMemMaxCapacitySize: 1572864
- SysMemPrimaryStatus: 1
- SysMemTotalSize: 65536
- SystemGeneration: 12G Monolithic
- SystemID: 1320
- SystemRevision: 0
- TempRollupStatus: 1
- UUID: 4c4c4544-0037-3810-8035-c6c04f325631
- VoltRollupStatus: 1
- smbiosGUID: 44454c4c-3700-1038-8035-c6c04f325631
Надеюсь, что это все еще полезно в вашем случае.
Спасибо вам, @Paul Crovella за ваш комментарий, это действительно заставило меня пойти по правильному пути. Я многому научился. В любом случае, решение оказалось комбинацией вещей. Этот пост был наиболее полезным, однако:
Как мне прочитать конверт ответа SOAP от PHP
Для тех из вас, кому интересно, как я это сделал, я в итоге написал функцию, которая проанализирует это для меня. Для вашего удобства он расположен ниже. Надеюсь, это поможет кому-то еще в будущем, кто ищет то же самое, что и я!
function pull_wsman_idrac($run_iDRAC_IP,$user,$pass,$namespace,$pullitem,$context="NULL"){
//Example pull_wsman("10.10.10.10","root","calvin","DCIM_SystemView","ServiceTag")
//Possible namespaces are listed on the DCIM profile page on Dell's website. http://en.community.dell.com/techcenter/systems-management/w/wiki/1906.dcim-library-profile
//Namspaces used (primary) : DCIM_SystemView, DCIM_CPUView, DCIM_ControllerView, DCIM_VFlashView, DCIM_MemoryView, DCIM_PCIDeviceView, DCIM_NICView, DCIM_IDRACCARDView
$wsman_output = shell_exec("wsman enumerate http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/root/dcim/$namespace -h $run_iDRAC_IP -V -v -c dummy.cert -P 443 -u $user -p $pass -j utf-8 -y basic");
$wsman_output = preg_replace('/\<\?xml version="1.0" encoding="UTF-8"\?\>/', '', $wsman_output);
$wsman_output = preg_replace('/\<\?xml version="1.0" encoding="utf-8"\?\>/', '', $wsman_output);
$wsman_output = trim($wsman_output);
$conv = <<<XML
<root> $wsman_output </root>
XML;
$xml = new SimpleXMLElement($conv);
$xml->registerXPathNamespace("s", "http://www.w3.org/2003/05/soap-envelope");
$xml->registerXPathNamespace("wsa", "http://schemas.xmlsoap.org/ws/2004/08/addressing");
$xml->registerXPathNamespace("wsen", "http://schemas.xmlsoap.org/ws/2004/09/enumeration");
$xml->registerXPathNamespace("n1", "http://schemas.dell.com/wbem/wscim/1/cim-schema/2/$namespace");
$xml->registerXPathNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
if($context == "NULL"){
$try = $xml->xpath("//s:$pullitem");
if(!empty($try)){ $cpath="s"; goto gotit; }
$try = $xml->xpath("//wsa:$pullitem");
if(!empty($try)){ $cpath="wsa"; goto gotit; }
$try = $xml->xpath("//wsen:$pullitem");
if(!empty($try)){ $cpath="wsen"; goto gotit; }
$try = $xml->xpath("//n1:$pullitem");
if(!empty($try)){ $cpath="n1"; goto gotit; }
$try = $xml->xpath("//xsi:$pullitem");
if(!empty($try)){ $cpath="xsi"; goto gotit; }
gotit:
if(empty($cpath)){ return 0; }
else{
$a = array();
$i = 0;
foreach($try as $tried){
foreach($tried->xpath("//n1:*") as $trys) {
$go = $trys->getName();
//$i++; echo "$i : " . $go . "\n";
$a[] = $go;
}
}
$a = array_unique($a);
return $a;
}
}
elseif($context !== "NULL"){
$try = $xml->xpath("//$context:$pullitem");
$a = array();
foreach($try as $extract){
$a[] = $extract;
}
if(!empty($a)){
return $a;
}
else{ return 0; }
}
}