Announcement

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

    Using valueXPath on a DataSourceField with mutltipe marked as true

    Hi,

    I've been trying to get something to work for a few days now, but i cannot seem to get the desired result by any means i try. I've got a datasource with a field configured like this:

    Code:
    <DataSource ID="Communication"
    	inheritsFrom="...." DataSourceVersion="1"
    	xmlFromConfig="true"
    	beanClassName="..."
    	dropExtraFields="true" titleField="subject" serverType="generic"
    	serverConstructor="...">
    	<fields>
    		<field name="subject" type="text" length="50" canHide="false"
    			required="true" width="*" colSpan="2" startRow="true">
    		</field>
    		<field name="category" valueXPath="category/id" type="Category"
    			editorType="select" optionDataSource="Category"
    			javaClass="..."
    			valueField="id" displayField="localizedLabel" width="*">
    		</field>
    		<field name="channel" valueXPath="channel/id" type="Channel"
    			editorType="select" optionDataSource="Channel"
    			javaClass="..."
    			valueField="id" displayField="localizedLabel" width="*">
    		</field>
    		<field name="content" type="text" editorType="textArea" width="*"
    			colSpan="2" startRow="true" detail="true">
    		</field>
    		<field name="participants" valueXPath="participants//id"
    			multiple="true" type="AbstractCommunicableObject" 
    			editorType="select" width="*" colSpan="2" startRow="true"
    			optionDataSource="AbstractCommunicableObject"
    			javaClass="..."
    			detail="true" hidden="false" ignore="false">
    		</field>
    	</fields>
    </DataSource>
    The valueXPath attribute works fine if you only want to have it retrieve a single value from a property. Like mentioned in the API of DataSourceField:

    Using valueXPath with the Smart GWT server

    If you're using the Smart GWT server to return data via the DSResponse object (or indirectly doing so using DataSource DMI), the valueXPath you specify on the DataSource fields will be applied to the data you return via the JXPath library.

    If you are returning Java Beans as your DSResponse data, normally each dataSource field receives the value of the same-named Java Bean property, that is, a field "zipCode" is populated by looking for "getZipCode()" on the objects passed as DSResponse data. You can use valueXPath to retrieve properties from subobjects, so long as a chain of getter methods exists that corresponds to the valueXPath. For example, a valueXPath of "address/zipCode" expects to call "getAddress()" on the bean(s) passed to DSResponse.setData(), followed by "getZipCode()" on whatever object "getAddress()" returns.

    When you are saving data, the inbound DSRequest values, available as a Java Map, will use just dataSource field names as Map keys, not the valueXPath used to derive them. However, to achieve bidirectional valueXPath binding, you can use the server-side method dataSource.setProperties() to use the valueXPath when setting properties on your server object model. When applied as a setter, an XPath like "address/zipCode" attempts "getAddress()" followed by "setZipCode()" on the returned object. JXPath also has some ability to auto-create intervening objects if they are missing, such as auto-creating an "address" subobject when applying "address/zipCode" as a valueXPath.

    See the JXPath library documentation for complete details, including other types of server object models supported, such as server-side XML.
    But what if you want to return a list of ID's instead of a single ID value? I only want the participants ID's to be returned instead of a entire object graph converted to JSON. As you can see in the code above i tried the JXPath expression to select each ID, but this only gives me back the first ID in the Set returned.

    Is there any way to get a list of ID's returned based on the ds.xml configuration? Or do i need to resort to the IToJson interface on each JavaBean where i want a Collection of elements to only return the id's of the elements inside it?

    Thanks in advance.

    #2
    As the docs suggest, see the JXPath documentation for samples of how you can do things like select multiple IDs from subobjects.

    Comment


      #3
      As suggested i took another look at the documentation of JXPath, but from what i read their i cannot directly solve the problem i have.

      The following code examples are taken from a article at JavaWorld, more precise http://www.javaworld.com/javaworld/jw-03-2007/jw-03-jxpath.html?page=1 . I also took a look at the more in detail tutorial linked from this article located at http://www.tfo-eservices.eu/wb_tutorials/pages/tutorials/jxpath-tutorial.php. But nothing what i read got me any further then before.

      The selection of single values is done in JXPath via code done like this :

      Code:
      Employee emp = (Employee)context.getValue("/departmentList/employees[name='Johnny']");
      But the selection of multiple values (in a collection) is done via another method of the JXPath context, namely iterate:

      Code:
       for(Iterator iter = context.iterate("/departmentList"); iter.hasNext();){
         Department d = (Department)iter.next();
         ....
      }
      (Also mentioned at http://commons.apache.org/jxpath/users-guide.html#Retrieving_Multiple_Results )


      I don't see how i can manipulate the switch of method getValue() to iterate() in the JXPath context via a simple declaration in the ds.xml file.

      In short to get a list of values via JXPath is would first have to iterate and get my collection back and then execute another JXPath query on the elements i got returned from the iterate value. But like mentioned i don't see any means of translating this into a single string to use in valueXPath.

      I tried the following valueXPath expressions:

      /participants/id
      /participants//id
      //participants/id
      //participants//id

      All of the above give me back the first id of the set. As a test i tried.

      /participants[2]/id

      which gives me back the second id of the set. All behavior is consistent with what a call to getValue() on the JXPath context would give me back.

      Am i missing some additional attributes which are available on the ds.xml file to do what i want to do? Or am i missing something in the documentation. because otherwise i can only conclude that their is no solution via the valueXPath attribute, since it looks like it only performs a getValue() call on the JXPath context.

      Thanks in advance

      Comment


        #4
        Interesting. We'll take a look at augmenting valueXPath to allow multiple values. In the meantime, consider either:

        - using nested DataSources: declare a second DataSource for participants, declare the "participants" field's type to match the ID of the DataSource for participants, and define only the ID field in the participants DataSource, setting dropExtraFields="true". What will be transferred is objects with a single property, available on the client side as Records

        - adding logic in your DataSource that will use JXPathContext.iterate based on a custom property "multipleValueXPath" that only your DataSource subclass knows about.

        Comment


          #5
          I've opted for the second suggestion. Here is what i've done to get things working:

          Extended the ds.xml metadata so that the field now contains a recordXPath attribute which will be used to iterate over the records:

          Code:
          <field name="participants" recordXPath="participants" valueXPath="id"
          		multiple="true" type="AbstractCommunicableObject" 
          		editorType="select" width="*" colSpan="2" startRow="true"
          		optionDataSource="AbstractCommunicableObject"
          		javaClass="..."
          		detail="true" hidden="false" ignore="false">
          </field>
          And then extended my datasource to see if the field has been marked with multiple, if so we use the recordXPath to get a pointer to the record and the valueXPath to get the value of the pointer. In code this gives something like:

          Code:
          if (field.getBoolean("multiple")) {
              final List values = new ArrayList();
              // Field is flagged with multiple, so we need to retrieve multiple values
              final String recordXPath = field.getProperty("recordXPath");
              for (final Iterator iter = context.iteratePointers(recordXPath); iter.hasNext();) {
              	final Pointer pointer = (Pointer) iter.next();
                  values.add(context.getRelativeContext(pointer).getValue(valueXPath));
              }
              result.put(fieldName, values);
          } else {
              // Retrieve the single value directly based on the valueXPath expression.
              result.put(fieldName, context.getValue(valueXPath));
          }
          Add some extra error handling and your done :-) When i now have a look at my data being send to the client it reads something like this:

          Code:
            data:[
                      {
                          category:9,
                          participants:[
                              4,
                              3,
                              1
                          ],
                          channel:10,
                          comment:null,
                          content:"This is a test message",
             .....
          Exactly what i wanted. Hope this tips helps others work around similar problems.
          Last edited by dvankeer; 15 Feb 2010, 03:42.

          Comment


            #6
            Time estimates on getting this feature in SmartGWT EE?

            We just met the similar issue with our Java object graph we planned to return using SmartGWT EE feature we are evaluating right now. It would be nice to have it in paid version.

            Comment


              #7
              An enhancement to make this automatic has been spec'd, but there is no ETA. To get the feature done on your timeline, consider Feature Sponsorship.

              Comment

              Working...
              X