Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Hierarchical XML data binding

    Hi,

    I've searched the forums and looked at the samples, but couldn't find a complete example on how to work with hierarchical XML.

    We have our services layer which returns a complete unit-of-work document represented as XML. That XML has a root node with many sub nodes which represent direct attributes, but also has collections which need to be fetched/saved using one single HTTP/XML query.

    A typical XML structure served by our services layer would look as follows:

    <root>
    <attrib1>value1</attrib1>
    <attrib2>value2</attrib2>
    <x_list>
    <x>
    <attrib3>value3</attrib3>
    <attrib4>value4</attrib4>
    </x>
    <x>
    <attrib3>value5</attrib3>
    <attrib4>value6</attrib4>
    </x>
    </x_list>
    <y_list>
    <y>
    ...
    </y>
    </y_list>
    </root>

    Using the same dataset, I want to bind some DynamicForm elements to attrib1 and attrib2 but I also want to have a ListGrid bind to x_list and another one bind to y_list. I don't want to have multiple server round trips as I want everything here to be handled within the same transaction or unit of work when data is saved.

    I've looked at nested Datasets, XPath, etc but some pieces are missing and couldn't find an actual complete example that illustrates how the SmartClient framework supports this hierarchical XML, if it does support it?

    Can someone please shed some light? Thanking you in advance for your help.

    Kind regards,

    #2
    Hi Yavery,

    Most of the examples are geared toward high data volume approaches, where you can't assume all data can be loaded in advance. Instead, to populate multiple components from a single XML document, in outline you'll want to:

    1) declare a DataSource per type of object that will be extracted from the XML, eg, one for the grid that will display x_list, one for the grid that will display y_list, one for the form for editing other attributes.

    2) load the document via isc.XMLTools.loadXML()

    3) in the callback to loadXML, for each "chunk" of the XML document that's going to be displayed by a different component:

    a) use isc.XMLTools.selectNodes() to extract each "chunk" as an Array of XMLElements

    b) use DataSource.recordsFromXML() to convert the XMLElements into JavaScript Objects. Note these objects will now have correctly typed values for any fields you've declared in the DataSource (eg field.type:"int" will produce a JavaScript Number)

    c) populate each component with these objects (ListGrid.setData(), DynamicForm.setValues(), etc)

    It's really not very much actual code - a partial example was posted by another user here. Note this example creates a ResultSet from data intended for a grid; this isn't strictly necessary but does allow client-side filtering in grids.

    To save, use DataSource.xmlSerialize() to put together a message to be saved from the modified data available in each of the components (eg DynamicForm.getValues(), ListGrid.getData()).

    Note further: if this is a WSDL-described web service, SmartClient can do more of the work:

    1) instead of loadXML, use WebService.callOperation(). For the "resultType" argument, ask for the outermost element that contains everything you're interested in (eg <root>); SmartClient will use the XML Schema embedded in the WSDL to decode everything in the message to correctly typed JavaScript and give it to you as a JSON structure. Use isc.logWarn(isc.echoFull(data)) to see what this structure looks like, then pass pieces of it to various components as with the results of recordFromXML() above.

    2) use WebService.callOperation() to call the save operation. As with the non-WSDL approach, you need to assemble all the data into an object to pass to xmlSerialize(), but you don't need to, eg, worry about the very complex rules for forming a correctly namespaced WSDL SOAP message; SmartClient takes care of that for you.

    Once you have a working example, sharing it will the community would be great; if you run into trouble, a real sample dataset would help in showing what you need to do.

    Comment


      #3
      Thanks,

      The provided information did help. I was able to easily split my XML into smaller XML pieces and have those consumed by each of my U/I components.

      Now I'm trying to figure out the "saving" aspect ... From my understanding, I need to do the reverse loading logic and gather back everything from each U/I component and re-assemble a complete XML unit-of-work XML document and save that assembled version.

      What about the following situation, assuming the following XML:

      Code:
      <root>
        <value1>1</value1>
        <value2>2</value2>
        <value3>3</value3>
        ...
      </root>
      And the following JSP:

      Code:
      <tabset>
        <tabs>
          <tab title="page 1">
             <pane>
                <DynamicForm ID="form1" dataSource="ds">
                    <field name="value1"/>
                </DynamicForm>
              </pane>
           </tab>
          <tab title="page 2">
             <pane>
                <DynamicForm ID="form2" dataSource="ds">
                    <field name="value1"/>
                    <field name="value2"/>
                </DynamicForm>
              </pane>
           </tab>
         </tabs>
      </tabset>
      Assuming, I've loaded page1 and page2 using the proposed approach (splitting the XML), they end up being bound to the same underlying data source in this particular case.

      As far as the merged XML is concerned (the XML that gets sent back to the server for saving), how will the following show-up in the XML (or will they show up at all?):

      - What happens to value1 (it's being referenced by 2 different U/I components - will it show up twice? if not which U/I component wins)?
      - What happens to value2 (that one is straight forward - single ref, I'm assuming it will show up in the XML stream as expected)?
      - What happens to value3, it's not being referenced by any of the U/I components (will it show up in the XML with its original value or will it not show at all)?

      Would the ValueManager help with any of this?

      Thanks for your help once again,

      Comment


        #4
        You've got two instances of value1 as fields in your form, but in your message there's only one..

        If all those properties belong to one logical object, yes use a ValuesManager with a single DataSource and multiple DynamicForm instances.

        Comment


          #5
          That's my point. What happens when a field is bound multiple times as well as not bound at all? When I use xmlSerialize() will that doubly bound field show up twice? And that other one which is not bound, will it not show at all?

          Thanks,

          Comment


            #6
            Take a look at the return value of DF.getValues(): an Object with one property per form field. Take a look at the example in the docs for xmlSerialize(): one element results from each property, with the element name based on the property name. So, all under your control in a very straightforward way.

            If you want elements <value1-3> to appear as peers in the XML, but you want to edit them with forms on separate tabs, then using a ValuesManager to combine two forms which are editing disjoint sets of fields makes sense, for example, form1 edits value1 and value2, and form2 edits value3. Combining these forms with a ValuesManager, ValuesManager.getValues() will return you:

            Code:
            { value1:"value", value2:"value", value3:"value" }
            .. which when passed to xmlSerialize() will result in XML like ..

            Code:
            <root>
                <value1>value</value1>
                <value2>value</value2>
                <value3>value</value3>
            </root>

            Comment


              #7
              Getting closer and closer (thanks for your help). Here's where I stand now ...

              My incoming XML looks like this (the outgoing needs to look like this too):

              Code:
              <xmldoc>
               <root>
                 <pkey>1</pkey>
                 <name>2</name>
                 <other>3</other>
                 <child_list>
                      <child>
                          <pkey>4</pkey>
                          <chdname>5</chdname>
                          <chdother>6</chdother>
                      </child>
                      <child>
                          <pkey>7</pkey>
                          <chdname>8</chdname>
                          <chdother>9</chdother>
                      </child>
                  </child_list>
                </root>
              </xmldoc>
              My datasets:

              Code:
              <DataSource
              	ID="rootDS"
              	dataFormat="xml"
              	recordXPath="//ROOT">
              
              	<fields>
              		<field name="PKEY"/>
              		<field name="NAME"/>
              		<field name="OTHER"/>
              	</fields>
              
              </DataSource>
              
              <DataSource
              	ID="childDS"
              	dataFormat="xml"
              	recordXPath="//CHILD">
              
              	<fields>
              		<field name="PKEY"/>
              		<field name="CHDNAME"/>
              		<field name="CHDOTHER"/>
              	</fields>
              
              </DataSource>
              I have the following U/I script (reduced for clarity):

              Code:
              <iso:loadDS ID="rootDS"/>
              <iso:loadDS ID="childDS"/>
              
              <iso:XML>
              
                      <ValuesManager ID="vm"/>
                      <DynamicForm ID="f1" dataSource="rootDS" valuesManager="vm">
                         <fields>
                            <field name="PKEY"/>
                            <field name="NAME"/>
                         </fields>
                      </DynamicForm>
              
                      <ListGrid ID="lstChild" dataSource="childDS"/>
                      <Button title="Submit" click="submit()"/>
              
              </iso:XML>
              And I have the following Javascript code:

              Code:
              //-----------------------------------------------------------------------------
              function populateDS (xmlDoc, xmlText)
              {
              	dataRoot = rootDS.recordsFromXML(isc.XMLTools.selectNodes(xmlDoc, "//ROOT"));
              	dataChild = childDS.recordsFromXML(isc.XMLTools.selectNodes(xmlDoc, "//CHILD"));
              
              	vm.setValues(dataRoot);
              	lstChild.setData(dataChild);
              }
              
              //-----------------------------------------------------------------------------
              function submit()
              {
              	var inputObject =
              	{
              		root : vm.getValues(),
              		child_list : listChild.data
              	};
              
              	var ds = isc.DataSource.create({ tagName : "xmldoc" });
              	var result = ds.xmlSerialize(inputObject);
              	alert(result);
              }
              
              isc.XMLTools.loadXML("http://127.0.0.1:8080/rest/getXmlData", "populateDS(xmlDoc, xmlText)");
              From the submit code, the resulting XML looks like the following:

              Code:
              <xmldoc>
                <root>
                 <pkey>1</pkey>
                 <name>2</name>
                 <other>3</other>
                 <child_list>
                      <child>
                          <elem>
                              <pkey>4</pkey>
                              <chdname>5</chdname>
                              <chdother>6</chdother>
                           </elem>
                           <elem>
                               <pkey>7</pkey>
                               <chdname>8</chdname>
                               <chdother>9</chdother>
                           </elem>
                      </child>
                  </child_list>
                 <child_list>
                     <pkey>4</pkey>
                     <chdname>5</chdname>
                     <chdother>6</chdother>
                 </child_list>
                 <child_list>
                     <pkey>7</pkey>
                     <chdname>8</chdname>
                     <chdother>9</chdother>
                  </child_list>
                </root>
              </xmldoc>
              I would have liked the submitted XML to respect the originating schema?

              - The child_list collection is repeated (once included within the root and others explicitely added from lstChild.data)
              - each child needs to be embedded within a <child> tag which in turn needs to be wrapped inside the collection wrapper <child_list> tag.

              Of course I could start navigating the XML dom to remove/cleanup things, but I'm sure I'm doing something wrong and there's an easy way to get this back on track.

              What am I doing wrong? Thanks again for your help and for being so patient with me.

              PS: I promise to post a complete working example for others to benefit from when I have it all figured out.

              Regards,

              Comment


                #8
                You can use declarations on the DataSources to control how JavaScript data is serialized to XML - this is basically the same engine that produces SOAP messages for WSDL. In this case this following declarations would work:

                Code:
                var inputObject =
                {
                    root : {
                         pkey:1,
                         name:2
                    },
                    child_list : [
                         { pkey:4, chdname:5 },
                         { pkey:5, chdname:6 }
                    ]
                };
                
                isc.DataSource.create({ ID:"childDS", tagName:"child" })
                var ds = isc.DataSource.create({ 
                      tagName : "xmldoc",
                      fields : [
                         { name:"child_list", multiple:true, type:"childDS" }
                      ]
                });
                ds.xmlSerialize(inputObject);

                Comment

                Working...
                X