2014-10-08 20 views
1

此問題已在許多變體中得到解答,但沒有一個涉及到我的情況。使用php將非標準xml wsman數據加載到對象中

我拉數據使用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> 
+0

嗨保羅,謝謝你的回覆,但你能詳細說明一下嗎?我正在瀏覽這本肥皂手冊,但到目前爲止,我沒有看到任何看起來像這樣的東西。我錯過了什麼?對不起,我從來沒有處理任何其他的東西,除了基本的 XML。我沒有意識到,上面是任何一種正常的... – Ethan 2014-10-09 03:11:41

回答

2

作爲響應/輸出得到的是多個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]); 

正如你現在有your're 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(..))]'); 

等等等等。

在自己的代碼,例如一個音符你給你的答案是:如果你在任何命名空間中尋找一個元素的名稱,你可以用local-name()做到這一點在XPath中:

$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 

希望這仍是你的情況有所幫助。

+0

哇,這是一個很好的解釋;和超級有用。我很感激你花時間做到這一點。 :)我做了與我使用的功能類似的東西,我可能會採取一些與您在此建議的相同的東西,以使其更有效。我的帽子是給你的,先生! – Ethan 2014-10-11 01:13:35

1

謝謝@Paul Crovella的評論,這實際上讓我走上了正軌。我最終學到了很多東西。無論如何,解決方案最終都是結合在一起的。這個職位是最幫助,但是:

How do I read SOAP reply Envelope by 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; } 
    } 
}