Reputation: 31
I used the XML Data Binding Wizard to create a class for an IRS XML specification file. I'm having two problems that I can't find an answer for (and I have spent hours trying).
This is a snippet of the XML output that I need:
<?xml version="1.0" encoding="UTF-8"?>
<h1:ACAUIBusinessHeader xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:irs="urn:us:gov:treasury:irs:common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:acabushdr="urn:us:gov:treasury:irs:msg:acabusinessheader" xmlns:h1="urn:us:gov:treasury:irs:msg:acauibusinessheader" xsi:schemaLocation="urn:us:gov:treasury:irs:msg:acauibusinessheader IRS-ACAUserInterfaceHeaderMessage.xsd">
<acabushdr:ACABusinessHeader>
<UniqueTransmissionId>00000000-0000-0000-0000-000000000000:SYS12:BB000::T</UniqueTransmissionId>
<irs:Timestamp>2001-12-17T09:30:47Z</irs:Timestamp>
</acabushdr:ACABusinessHeader>
<ACATransmitterManifestReqDtl>
<PaymentYr>1000</PaymentYr>
<PriorYearDataInd>1</PriorYearDataInd>
<irs:EIN>000000000</irs:EIN>
</ACATransmitterManifestReqDtl>
</h1:ACATransmitterManifestReqDtl>
As you can see the root node does not use the default xmlns namespace. If I add the "h1:" prefix to the name in GetDocBinding() calls then it adds the "h1:" prefix to ALL the child nodes which is NOT what I need.
Also I manually edited the class that the data binding wizard created so that I can define the extra namespaces using the DeclareNamespace() method. Yet when I try to use the optional third parameter of the RegisterChildNode() method to define the URI, it crashes when I try to use the property for that node.
Here's a code snippet of the edited class:
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall GetACAUIBusinessHeader(Xml::Xmlintf::_di_IXMLDocument Doc)
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Doc->GetDocBinding("ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), eACA_UI_Header_TargetNamespace);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall GetACAUIBusinessHeader(Xml::Xmldoc::TXMLDocument *Doc)
{
Xml::Xmlintf::_di_IXMLDocument DocIntf;
Doc->GetInterface(DocIntf);
return GetACAUIBusinessHeader(DocIntf);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall LoadACAUIBusinessHeader(const System::UnicodeString& FileName)
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Xml::Xmldoc::LoadXMLDocument(FileName)->GetDocBinding("ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), eACA_UI_Header_TargetNamespace);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall NewACAUIBusinessHeader()
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Xml::Xmldoc::NewXMLDocument()->GetDocBinding("ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), eACA_UI_Header_TargetNamespace);
};
// TXMLTransmitterACAUIBusinessHeaderType
void __fastcall TXMLTransmitterACAUIBusinessHeaderType::AfterConstruction(void)
{
RegisterChildNode(System::UnicodeString("ACABusinessHeader"), __classid(TXMLACABulkBusinessHeaderRequestType_air7_0), "urn:us:gov:treasury:irs:common");
RegisterChildNode(System::UnicodeString("ACATransmitterManifestReqDtl"), __classid(TXMLACATrnsmtManifestReqDtlType_air7_0));
Xml::Xmldoc::TXMLNode::AfterConstruction();
};
UnicodeString __fastcall TXMLTransmitterACAUIBusinessHeaderType::Get_irs_NS()
{
return TXMLNode::AttributeNodes->Nodes[UnicodeString("xmlns:irs")]->Text;
};
void __fastcall TXMLTransmitterACAUIBusinessHeaderType::Set_irs_NS(UnicodeString Value)
{
//TXMLNode::SetAttributeNS(UnicodeString("xmlns:irs"), "", Value);
TXMLNode::DeclareNamespace(UnicodeString("irs"), Value);
};
// More of the same
And here is some preliminary code I wrote to test the output using the class:
Manifest = new TXMLDocument(GetWindow());
Manifest->DOMVendor = GetDOMVendor("MSXML"); // NOT cross platform compatible
Manifest->NodeIndentStr = char(9); // TAB character
Manifest->Options = Manifest->Options << doNodeAutoIndent << doAttrNull << doAutoPrefix;
Manifest_root = GetACAUIBusinessHeader(Manifest);
Manifest->Version = "1.0";
Manifest->Encoding = "UTF-8";
// Assign NameSpaces and Schema
Manifest_root->irs_NS = "urn:us:gov:treasury:irs:common";
Manifest_root->xsi_NS = "http://www.w3.org/2001/XMLSchema-instance";
Manifest_root->acabushdr_NS = "urn:us:gov:treasury:irs:msg:acabusinessheader";
Manifest_root->h1_NS = "urn:us:gov:treasury:irs:msg:acauibusinessheader";
Manifest_root->schemaLocation = "urn:us:gov:treasury:irs:msg:acauibusinessheader IRS-ACAUserInterfaceHeaderMessage.xsd";
Manifest_root->ACABusinessHeader->UniqueTransmissionId = "token"; // Just for testing
Manifest_root->ACABusinessHeader->Timestamp = "2001-12-17T09:30:47Z"; // Just for testing
Manifest_root->ACATransmitterManifestReqDtl->PaymentYr = "2016";
Manifest_root->ACATransmitterManifestReqDtl->PriorYearDataInd = "1";
Manifest_root->ACATransmitterManifestReqDtl->EIN = "000000000";
If I take out the third parameter for the RegisterChildNode() method in the code above it doesn't crash but the output isn't correct:
<?xml version="1.0" encoding="UTF-8"?>
<ACAUIBusinessHeader xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:irs="urn:us:gov:treasury:irs:common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:acabushdr="urn:us:gov:treasury:irs:msg:acabusinessheader" xmlns:h1="urn:us:gov:treasury:irs:msg:acauibusinessheader" xsi:schemaLocation="urn:us:gov:treasury:irs:msg:acauibusinessheader IRS-ACAUserInterfaceHeaderMessage.xsd">
<ACABusinessHeader>
<UniqueTransmissionId>token</UniqueTransmissionId>
<Timestamp>2001-12-17T09:30:47Z</Timestamp>
</ACABusinessHeader>
<ACATransmitterManifestReqDtl>
<PaymentYr>2016</PaymentYr>
<PriorYearDataInd>1</PriorYearDataInd>
<EIN>000000000</EIN>
</ACATransmitterManifestReqDtl>
</ACAUIBusinessHeader>
As you can see the prefixes (namespaces) I need for the root node ACAUIBusinessHeader, the ACABusinessHeader node, and the Timestamp & EIN nodes are missing, and I don't know how to properly adapt the class to assign them.
Upvotes: 1
Views: 1115
Reputation: 31
I was finally able to figure out how to adapt the generated XML classes to get the desired output. I moved the DeclareNamespace() calls to the AfterConstruction() function, and had to change the Get() function calls (and Set() when applicable) for those nodes that used different namespaces.
Here's the first part of the adapted code:
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall GetACAUIBusinessHeader(Xml::Xmlintf::_di_IXMLDocument Doc)
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Doc->GetDocBinding("n1:ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), nsN1);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall GetACAUIBusinessHeader(Xml::Xmldoc::TXMLDocument *Doc)
{
Xml::Xmlintf::_di_IXMLDocument DocIntf;
Doc->GetInterface(DocIntf);
return GetACAUIBusinessHeader(DocIntf);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall LoadACAUIBusinessHeader(const System::UnicodeString& FileName)
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Xml::Xmldoc::LoadXMLDocument(FileName)->GetDocBinding("n1:ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), nsN1);
};
_di_IXMLTransmitterACAUIBusinessHeaderType __fastcall NewACAUIBusinessHeader()
{
return (_di_IXMLTransmitterACAUIBusinessHeaderType) Xml::Xmldoc::NewXMLDocument()->GetDocBinding("n1:ACAUIBusinessHeader", __classid(TXMLTransmitterACAUIBusinessHeaderType), nsN1);
};
// TXMLTransmitterACAUIBusinessHeaderType
void __fastcall TXMLTransmitterACAUIBusinessHeaderType::AfterConstruction(void)
{
TXMLNode::DeclareNamespace(System::UnicodeString(""), nsACA); // default xmlns
TXMLNode::DeclareNamespace(System::UnicodeString("irs"), nsIRS);
TXMLNode::DeclareNamespace(System::UnicodeString("xsi"), nsXSI);
TXMLNode::DeclareNamespace(System::UnicodeString("acabushdr"), nsACAbushdr);
TXMLNode::DeclareNamespace(System::UnicodeString("n1"), nsN1);
RegisterChildNode(System::UnicodeString("ACABusinessHeader"), __classid(TXMLACABulkBusinessHeaderRequestType_air7_0), nsACAbushdr);
RegisterChildNode(System::UnicodeString("ACATransmitterManifestReqDtl"), __classid(TXMLACATrnsmtManifestReqDtlType_air7_0), nsACA);
TXMLNode::SetAttribute(System::UnicodeString("xsi:schemaLocation"), SchemaLoc);
Xml::Xmldoc::TXMLNode::AfterConstruction();
};
_di_IXMLACABulkBusinessHeaderRequestType_air7_0 __fastcall TXMLTransmitterACAUIBusinessHeaderType::Get_ACABusinessHeader()
{
_di_IXMLACABulkBusinessHeaderRequestType_air7_0 result = GetChildNodes()->FindNode("acabushdr:ACABusinessHeader", nsACAbushdr);
if(result == NULL)
result = GetChildNodes()->Nodes[System::UnicodeString("acabushdr:ACABusinessHeader")]; // creates it
return result;
};
_di_IXMLACATrnsmtManifestReqDtlType_air7_0 __fastcall TXMLTransmitterACAUIBusinessHeaderType::Get_ACATransmitterManifestReqDtl()
{
_di_IXMLACATrnsmtManifestReqDtlType_air7_0 result = GetChildNodes()->FindNode("ACATransmitterManifestReqDtl", nsACA);
if(result == NULL) // Because this XML document doesn't use the default xmlns for the "root" node, but this node DOES use the default xmlns, it has to be added this way to match up the namespace
result = AddChild("ACATransmitterManifestReqDtl", nsACA); // creates it
return result;
};
The "nsACA" and its cousins are just constant UnicodeStrings. As you can see I also had to add the "n1:" prefix/namespace to the name of the root node in the GetDocBinding() calls. The namespace is later declared in the AfterConstruction() function. For the nodes who use the "irs:" prefix/namespace it was enough to simply add it to the node string name after it was declared in the AfterConstruction() function. Here's an example:
System::UnicodeString __fastcall TXMLACABulkBusinessHeaderRequestType_air7_0::Get_Timestamp()
{
return GetChildNodes()->Nodes[System::UnicodeString("irs:Timestamp")]->NodeValue.operator System::UnicodeString();
};
void __fastcall TXMLACABulkBusinessHeaderRequestType_air7_0::Set_Timestamp(System::UnicodeString Value)
{
GetChildNodes()->Nodes[System::UnicodeString("irs:Timestamp")]->NodeValue = Value;
};
There's a little more involved when one of the child nodes doesn't inherit it's namespace from the parent node and is using the default xmlns namespace (which doesn't have a prefix like the "irs:" example). Here's how I handled that case for the UniqueTransmissionId node:
System::UnicodeString __fastcall TXMLACABulkBusinessHeaderRequestType_air7_0::Get_UniqueTransmissionId()
{
_di_IXMLNode result = GetChildNodes()->FindNode("UniqueTransmissionId", nsACA);
if(result == NULL)
return EmptyStr;
return result->NodeValue.operator System::UnicodeString();
//return GetChildNodes()->Nodes[System::UnicodeString("UniqueTransmissionId")]->NodeValue.operator System::UnicodeString();
};
void __fastcall TXMLACABulkBusinessHeaderRequestType_air7_0::Set_UniqueTransmissionId(System::UnicodeString Value)
{
_di_IXMLNode result = GetChildNodes()->FindNode("UniqueTransmissionId", nsACA);
if(result == NULL) // Because this XML document doesn't use the default xmlns for the "root" node, but this node DOES use the default xmlns, it has to be added this way to match up the namespace
result = AddChild("UniqueTransmissionId", nsACA); // creates it
result->NodeValue = Value;
//GetChildNodes()->Nodes[System::UnicodeString("UniqueTransmissionId")]->NodeValue = Value;
};
And here is the final output:
<?xml version="1.0" encoding="UTF-8"?>
<n1:ACAUIBusinessHeader xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:irs="urn:us:gov:treasury:irs:common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:acabushdr="urn:us:gov:treasury:irs:msg:acabusinessheader" xmlns:n1="urn:us:gov:treasury:irs:msg:acauibusinessheader" xsi:schemaLocation="urn:us:gov:treasury:irs:msg:acauibusinessheader IRS-ACAUserInterfaceHeaderMessage.xsd">
<acabushdr:ACABusinessHeader>
<UniqueTransmissionId>token</UniqueTransmissionId>
<irs:Timestamp>2001-12-17T09:30:47Z</irs:Timestamp>
</acabushdr:ACABusinessHeader>
<ACATransmitterManifestReqDtl>
<PaymentYr>2016</PaymentYr>
<PriorYearDataInd>1</PriorYearDataInd>
<irs:EIN>000000000</irs:EIN>
</ACATransmitterManifestReqDtl>
</n1:ACAUIBusinessHeader>
Hopefully this will prove helpful to anyone working with the Delphi/C++ Builder XML Data Binding Wizard. It appears that it can only handle a single namespace on its own, but with these sorts of changes it can still be a good place to start from.
Upvotes: 1