<tag></tag>
<tag />
那麼當使用 gSoap 建議的 onvif schema 產生程式碼後,如何確認哪些函數會生成 <tag />的形式呢?
gSOAP 的 stdsoap2.c中會透過soap_element_start_end_out()函數產生 "/>" 的標籤,而透過 onvif schema 所產生的程式碼,主要會透過呼叫 stdsoap2.c其中的三個函數 soap_element_href(), soap_element_null(), soap_element_nil()來產生 "/>" 的標籤。
由於 gSoap 會自動根據 wsdl 產生 header file, 接著自動產生程式碼。以下針對 gSOAP 產生 empty element tag "/>" 的規則作一整理
因此若我們想要強制其產生</>,則不應該手動呼叫上述的三個函數,
而應該了解其生成原理。藉著修改wsdl, 或是 header file, 來達成此功能。
一、若某個 struct 內只有一個 reuqired element, 則可以產生 "/>" 以下試舉兩例:
例1: 可以產生 <tt:Message/>
1 struct _wsnt__NotificationMessageHolderType_Message 2 { 3 struct _tt__Message* tt__Message_ 4 1; 5 } Message 1; ///< Required element.
則對應產生的程式碼便會產生 "/>"
1 SOAP_FMAC3 int SOAP_FMAC4 2 soap_out__wsnt__NotificationMessageHolderType_Message( 3 struct soap *soap, const char *tag, int id, const struct 4 _wsnt__NotificationMessageHolderType_Message *a, const 5 char *type) 6 { 7 (void)soap; (void)tag; (void)id; (void)type; 8 if (soap_element_begin_out(soap, tag, soap_embedded_id( 9 soap, id, a, 10 SOAP_TYPE__wsnt__NotificationMessageHolderType_Message) 11 , type)) 12 return soap->error; 13 if (a->tt__Message_) 14 { if (soap_out_PointerTo_tt__Message(soap, "tt: 15 Message", -1, &a->tt__Message_, "")) 16 return soap->error; 17 } 18 else if (soap_element_nil(soap, "tt:Message")) 19 return soap->error; 20 return soap_element_end_out(soap, tag); 21 }
例2:
1 struct _trt__RemoveVideoEncoderConfiguration 2 { 3 tt__ReferenceToken ProfileToken 4 1; ///< Required element. 5 };
則對應產生的程式碼便會產生 "/>"
1 SOAP_FMAC3 int SOAP_FMAC4 2 soap_out__trt__RemoveVideoEncoderConfiguration(struct 3 soap *soap, const char *tag, int id, const struct 4 _trt__RemoveVideoEncoderConfiguration *a, const char 5 *type) 6 { 7 (void)soap; (void)tag; (void)id; (void)type; 8 if (soap_element_begin_out(soap, tag, soap_embedded_id( 9 soap, id, a, 10 SOAP_TYPE__trt__RemoveVideoEncoderConfiguration), type) 11 ) 12 return soap->error; 13 if (a->ProfileToken) 14 { if (soap_out_tt__ReferenceToken(soap, "trt: 15 ProfileToken", -1, &a->ProfileToken, "")) 16 return soap->error; 17 } 18 else if (soap_element_nil(soap, "trt:ProfileToken")) 19 return soap->error; 20 return soap_element_end_out(soap, tag); 21 }
二. 若 struct 內有超過一個 required element ,則仍能可以產生 <tt:Message/>
對應產生的程式碼如下:1 struct _trt__AddVideoSourceConfiguration 2 { 3 tt__ReferenceToken ProfileToken 4 1; ///< Required element. 5 tt__ReferenceToken 6 ConfigurationToken 1; ///< Required 7 element. 8 };
1 SOAP_FMAC3 int SOAP_FMAC4 2 soap_out__trt__AddVideoSourceConfiguration(struct soap 3 *soap, const char *tag, int id, const struct 4 _trt__AddVideoSourceConfiguration *a, const char *type) 5 { 6 (void)soap; (void)tag; (void)id; (void)type; 7 if (soap_element_begin_out(soap, tag, soap_embedded_id( 8 soap, id, a, 9 SOAP_TYPE__trt__AddVideoSourceConfiguration), type)) 10 return soap->error; 11 if (a->ProfileToken) 12 { if (soap_out_tt__ReferenceToken(soap, "trt: 13 ProfileToken", -1, &a->ProfileToken, "")) 14 return soap->error; 15 } 16 else if (soap_element_nil(soap, "trt:ProfileToken")) 17 return soap->error; 18 if (a->ConfigurationToken) 19 { if (soap_out_tt__ReferenceToken(soap, "trt: 20 ConfigurationToken", -1, &a->ConfigurationToken, "")) 21 return soap->error; 22 } 23 else if (soap_element_nil(soap, "trt: 24 ConfigurationToken")) 25 return soap->error; 26 return soap_element_end_out(soap, tag); 27 }
三. 若 struct 內對應的 element 是可以0~多個,並且有對應的 size 資料結構,則無法產生 <tt:Message/>
1 struct _wsnt__SystemMessageHolderType_Message 2 { 3 $int __sizett__Message_ 4 0; 5 struct _tt__Message* tt__Message_ 6 0; 7 } Properties 8 1; ///< Required element.
即使全部改成 reuqired element, 還是不能
1 struct _wsnt__SystemMessageHolderType_Message 2 { 3 $int __sizett__Message_ 4 1; 5 struct _tt__Message* tt__Message_ 6 1; 7 }Properties 1; ///< Required 8 element.
對應產生的程式碼如下:
四、若同時有 Required element 以及 1~多個的 element 存在,則 Required element 仍可以正常生成 "/>"。1 SOAP_FMAC3 int SOAP_FMAC4 2 soap_out__wsnt__SystemMessageHolderType_Message(struct 3 soap *soap, const char *tag, int id, const struct 4 _wsnt__SystemMessageHolderType_Message *a, const char 5 *type) 6 { 7 (void)soap; (void)tag; (void)id; (void)type; 8 if (soap_element_begin_out(soap, tag, soap_embedded_id( 9 soap, id, a, 10 SOAP_TYPE__wsnt__SystemMessageHolderType_Message), 11 type)) 12 return soap->error; 13 if (a->tt__Message_) 14 { int i; 15 for (i = 0; i < a->__sizett__Message_; i++) 16 if (soap_out__tt__Message(soap, "tt:Message", -1, 17 a->tt__Message_ + i, "")) 18 return soap->error; 19 } 20 return soap_element_end_out(soap, tag); 21 }
1 struct _tan__ModifyRules 2 { 3 /// @brief Reference to an existing 4 VideoAnalyticsConfiguration. 5 /// Element ConfigurationToken of type "http://www. 6 onvif.org/ver10/schema":ReferenceToken. 7 tt__ReferenceToken 8 ConfigurationToken 1; ///< Required 9 element. 10 /// Size of array of struct tt__Config* is 1.. 11 unbounded 12 $int __sizeRule 13 1; 14 /// Array struct tt__Config* of length 1..unbounded 15 struct tt__Config* Rule 16 1; 17 };
對應產生的程式碼如下:
1 SOAP_FMAC3 int SOAP_FMAC4 soap_out__tan__ModifyRules( 2 struct soap *soap, const char *tag, int id, const struct 3 _tan__ModifyRules *a, const char *type) 4 { 5 (void)soap; (void)tag; (void)id; (void)type; 6 if (soap_element_begin_out(soap, tag, soap_embedded_id( 7 soap, id, a, SOAP_TYPE__tan__ModifyRules), type)) 8 return soap->error; 9 if (a->ConfigurationToken) 10 { if (soap_out_tt__ReferenceToken(soap, "tan: 11 ConfigurationToken", -1, &a->ConfigurationToken, "")) 12 return soap->error; 13 } 14 else if (soap_element_nil(soap, "tan: 15 ConfigurationToken")) 16 return soap->error; 17 if (a->Rule) 18 { int i; 19 for (i = 0; i < a->__sizeRule; i++) 20 if (soap_out_tt__Config(soap, "tan:Rule", -1, a-> 21 Rule + i, "")) 22 return soap->error; 23 } 24 return soap_element_end_out(soap, tag); 25 }
五、結論:只要 gSoap產生的 header file 內註明是 Required element,並且此 require element 只會有一個的情況。便可以正常生成 "/>"。但是如果我想要多個 element 都會生成 "/>",例如讓下列例子中的SimpleItem可以變成 <SimpleItem/>,是否有可能呢??或者該問,這樣做是否仍符合原先的 WSDL 所定義的規範??
以 SimpleItem 為例,其產生的 XML 舉例如下:
1 <tt:SimpleItem Name="Source" Value="11"></tt:SimpleItem> 2 <tt:SimpleItem Name="State" Value="22"></tt:SimpleItem>
我們試圖要讓其變成
1 <tt:SimpleItem Name="Source" Value="11"/> 2 <tt:SimpleItem Name="State" Value="22"/>
其在 mime.h 內定義為
1 struct tt__ItemList 2 { 3 $int __sizeSimpleItem 4 0; 5 struct _tt__ItemList_SimpleItem 6 { 7 /// @brief Item name. 8 /// Attribute Name of type xs:string. 9 @char* Name 10 1; ///< Required attribute. 11 12 /// @brief Item value. The type is defined in the 13 corresponding description. 14 /// Attribute Value of type xs:anySimpleType. 15 @xsd__anySimpleType Value 16 1; ///< Required attribute. 17 18 } *SimpleItem 19 0; 20 21 /// @brief Complex value structure. 22 /// Size of ElementItem array is 0..unbounded 23 $int 24 __sizeElementItem 0; 25 struct _tt__ItemList_ElementItem 26 { 27 @char* Name 28 1; ///< Required attribute. 29 30 } *ElementItem 31 0; 32 33 struct tt__ItemListExtension* Extension 34 0; ///< Optional element. 35 36 };
對應產生的程式碼如下:
測試如下:1 SOAP_FMAC3 int SOAP_FMAC4 soap_out_tt__ItemList(struct 2 soap *soap, const char *tag, int id, const struct 3 tt__ItemList *a, const char *type) 4 { 5 (void)soap; (void)tag; (void)id; (void)type; 6 if (soap_element_begin_out(soap, tag, soap_embedded_id( 7 soap, id, a, SOAP_TYPE_tt__ItemList), type)) 8 return soap->error; 9 if (a->SimpleItem) 10 { int i; 11 for (i = 0; i < a->__sizeSimpleItem; i++) 12 if (soap_out__tt__ItemList_SimpleItem(soap, "tt: 13 SimpleItem", -1, a->SimpleItem + i, "")) 14 return soap->error; 15 } 16 if (a->ElementItem) 17 { int i; 18 for (i = 0; i < a->__sizeElementItem; i++) 19 if (soap_out__tt__ItemList_ElementItem(soap, "tt: 20 ElementItem", -1, a->ElementItem + i, "")) 21 return soap->error; 22 } 23 if (soap_out_PointerTott__ItemListExtension(soap, "tt: 24 Extension", -1, &a->Extension, "")) 25 return soap->error; 26 return soap_element_end_out(soap, tag); 27 }
1. 將 SimpleItem 改為 reuqire element,改成以下定義,仍然不行。
2. 移除 __sizeSimpleItem,則可以正確產生。所以問題在於 gSoap 的 code generate rule。1 $int __sizeSimpleItem 2 0; 3 struct _tt__ItemList_SimpleItem 4 { 5 @xsd__anySimpleType Value 6 0; ///< Required attribute. 7 @char* Name 8 0; ///< Required attribute. 9 } *SimpleItem 10 1;
3. 假設我們手動更改程式如下: 仍然有問題。
1 for (i = 0; i < a->__sizeSimpleItem; i++) 2 { 3 if (a->SimpleItem + i) 4 { 5 if (soap_out__tt__ItemList_SimpleItem(soap, "tt: 6 SimpleItem", -1, a->SimpleItem + i, "")) 7 return soap->error; 8 } 9 else if (soap_element_nil(soap, "tt:SimpleItem")) 10 return soap->error; 11 }
此改法可能會產生類似下列的結果,這也不是我想要的。
4. 因為 _tt__ItemList_SimpleItem 定義內只有 attribute 並不存在 element,所以其實可以將其看成總是不會有 element, 每次呼叫 soap_out__tt__ItemList_SimpleItem() 輸出時,都自動生成 "/>" ,可參考以下程式1 <tt:SimpleItemDescription/> 2 <tt:SimpleItemDescription/>
1 SOAP_FMAC3 int SOAP_FMAC4 2 soap_out__tt__ItemList_SimpleItem(struct soap *soap, 3 const char *tag, int id, const struct 4 _tt__ItemList_SimpleItem *a, const char *type) 5 { 6 if (a->Value) 7 soap_set_attr(soap, "Value", a->Value, 1); 8 if (a->Name) 9 soap_set_attr(soap, "Name", a->Name, 1); 10 (void)soap; (void)tag; (void)id; (void)type; 11 if (soap_element_begin_out(soap, tag, 12 soap_embedded_id(soap, id, a, 13 SOAP_TYPE__tt__ItemList_SimpleItem), type)) 14 return soap->error; 15 return soap_element_end_out(soap, tag); 16 }
現在程式碼針對此情況會利用 soap_element_begin_out(), 若此時直接改成呼叫 soap_element_nil(),應該有機會達成我所想要的功能。
5. 程式碼若更改如下,可以達到預期功能。
1 SOAP_FMAC3 int SOAP_FMAC4 soap_out__tt__ItemList_SimpleItem( 2 struct soap *soap, const char *tag, int id, const struct 3 _tt__ItemList_SimpleItem *a, const char *type) 4 { 5 if (a->Value) 6 soap_set_attr(soap, "Value", a->Value, 1); 7 if (a->Name) 8 soap_set_attr(soap, "Name", a->Name, 1); 9 (void)soap; (void)tag; (void)id; (void)type; 10 #if 1 // 20130805 albert.liao modified for force short 11 empty element tag 12 if (soap_element_nil(soap, "tt:SimpleItem")) 13 return soap->error; 14 #else 15 if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, 16 id, a, SOAP_TYPE__tt__ItemList_SimpleItem), type)) 17 return soap->error; 18 return soap_element_end_out(soap, tag); 19 #endif 20 }
產生的 xml 如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <wsnt:Notify> 3 <wsnt:NotificationMessage> 4 <wsnt:Message> 5 <tt:Message UtcTime="1970-01-01T00:00:00Z"> 6 <tt:Source><tt:SimpleItem Name="Name" Value="Value"/></tt:Source> 7 </tt:Message> 8 </wsnt:Message> 9 </wsnt:NotificationMessage> 10 </wsnt:Notify>
六、經過程式分析之後,我認為應該是在 wsdl2h 執行時,需要指定某種 flag,讓gSOAP知道此 Tag 只有定義 property,並沒有定義 element,因此在呼叫 soap_element_begin_out() 或是 soap_element_end_out() 時便可以直接套用 empty element tag。但目前並沒有發現此種 flag 可供操作。
參考資料: