Integrations and APIs for Service

Get Involved. Join the Conversation.

Topic

    James Whitehouse
    Accessing custom fields via SOAP
    Topic posted October 31, 2012 by James Whitehouse 
    2835 Views, 14 Comments
    Title:
    Accessing custom fields via SOAP
    Content:

    I performed a GET for incidents based on the sample code described in the GET documentation, but it does not return the custom fields.

    http://community.rightnow.com/developer/fileexchange/Connect_Web_Services_for_SOAP_August_2012/Content/Web%20Service%20API/Operations/Get.htm

    The documentation for Object Model Introduction indicates that custom fields are only returned when specified, but it does not indicate how to specify their return:

    http://community.rightnow.com/developer/fileexchange/Connect_Web_Services_for_SOAP_August_2012/Content/Web%20Service%20API/Object%20Model/Object%20Model%20Introduction.htm

    Connect Web Services exposes custom fields in a similar manner to all other object properties. As custom fields can be organized and deployed in packages, the first layer of the CustomFields property on objects will represent the package details for which the custom field is defined and deployed. Within each of these properties is a list of all the individual custom field properties. Custom fields are returned only when specified during a Get operation. Since the type of the custom field is not known at compile time, the representation of each of the custom fields supported is done through the use of GenericField objects.

    Any suggestions on how to get custom fields via the SOAP APIs?

    Thanks
    James

     

    Version:
    Feb 2012

    Comment

     

    • linda postniece

      The following is a sample query using the QueryCSV method:

      SELECT *, CustomFields.CO.*, CustomFields.c.* FROM Task

    • James Whitehouse

      Thanks Linda, but I am looking for something that works via the SOAP APIs rather than via the ROQL queries. 

    • linda postniece

      The following SOAP envelope fragment works for me and returns all custom fields for Contact:

       

       

         <soapenv:Body>
            <v1:QueryObjects>
               <v1:Query>SELECT Contact FROM Contact WHERE ID = 1;</v1:Query>
        <v1:ObjectTemplates xmlns:ns4="urn:objects.ws.rightnow.com/v1_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns4:Contact">
                      <ns4:CustomFields />
                      <ns4:Emails />
                  </v1:ObjectTemplates>
            </v1:QueryObjects>
         </soapenv:Body>
    • Allan Schrum

      The basic idea is to put in an empty tag called "CustomFields" similar to this fragment:

          <rnm:Get>
              <rnm:RNObjects xsi:type='rno:Contact'>
                  <rnb:ID id='1' />
                  <rno:CustomFields />     <!-- an empty tag means "specify to get" which will then return this content in the response -->
              </rnm:RNObjects>
          </rnm:Get>
       

      Depending upon the language you use it typically means creating an empty CustomFields sub-object (i.e. just do the "new" and assign / set it to the CustomFields property). The name of the custom fields sub-object will vary (e.g. ContactCustomFields) so let your editor for your bindings help you.

      The ROQL query above mimics this same approach. In the object templates you place the equivalent of a Get request for your object so that you can specify which sub-objects you wish. The difference is that ROQL can fetch objects using criteria different that simply the ID or LookupName.

      Regards,

      -Allan

    • Ayush Vora

      Why not?

       

      Incident inc = new Incident();
      //of course, you'd have to set the ID as well
      
      inc.setCustomFields(new GenericField[]{});
      client.get(new RNObject[]{inc},getProcessingOptions, clientInfoHeader);
      
    • Allan Schrum

      It depends upon which version you are using of Connect. For version 1 and 1.1 your example is correct. For version 1.2, change GenericField to GenericObject and it works just fine.

      Regards,

      -Allan

    • James Whitehouse

      Allan, is any of this information available in the documentation and I just missed it?  While the community is always helpful, it would be good to have a better understanding of the basics.

      Also, is it a general SOAP convention to attach empty objects to a GET to specify desired outputs?  It seems a bit odd to me, but I am more of a RESTful services guy, so I am just feeling my way through the SOAP services.

      Thanks

      James

       

    • Allan Schrum

      The change in the custom fields type was mentioned in the release notes. It could be expanded upon there but we've been adding comments to the forums to supplement that information.

      The get-by-example or specify-to-get concept is something that we've had since the very beginning of Connect Web Services for SOAP. The "specify-to-get" concept is described in the "Connect Web Services" => "Operational Behavior" => "Operational Overview" => "CRUD Behavior" section.

      This is a fairly novel way to use SOAP services. Typical requests are <noun><verb> method names for many other SOAP services. For us, using only the <verb> method approach makes the requests more efficient over the wire (as you know, XML and SOAP are very verbose). It makes it a bit more tricky for some automated integration tools, but for those types of systems we recommend the Generic WSDL approach rather than the typed WSDL. With more efficient SOAP processing it helps our servers and helps our client's code as well.

      Regards,

      -Allan

       

    • Anurag Mittal

      Hi Allan,

      I have a requirement where I need to fetch all custom fields of Contact/Incident Object through .NET(Add-Ins). I tried the following code as per the above suggestion but I am only getting different packages name of  CustomFields not the actual fields.

                 Contact contact = new Contact();
                  ID contactId = new ID();
                  contactId.id = 1762;
                  contactId.idSpecified = true;
                  contact.ID = contactId;
                  contact.CustomFields = new GenericObject{ };// to get custom fields

                  GetProcessingOptions gpo = new GetProcessingOptions();
                  gpo.FetchAllNames = true;

                  ClientInfoHeader clientInfoHeader = new ClientInfoHeader();
                  clientInfoHeader.AppID = "Basic Generic Read Sample";

                  RNObject[] results = client.Get(clientInfoHeader, new RNObject[] { contact }, gpo);

      I did a work around and based on above packages have written a 'QueryCSV' to get all CustomFields and its working (able to get all custom fields dynamically). But I don't know whether there is any other easy way to do this (I don't want to hard code anything not even package). Below is the full code that I have written, could you please suggest whether I am on the right track or there is better way?

       

                  EndpointAddress endPointAddr = new EndpointAddress(_globalContext.GetInterfaceServiceUrl(ConnectServiceType.Soap));
                  BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
                  binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
                  binding.MaxReceivedMessageSize = 1024 * 1024;
                  binding.MaxBufferSize = 1024 * 1024;
                  binding.MessageEncoding = WSMessageEncoding.Mtom;
                  RightNowSyncPortClient client = new RightNowSyncPortClient(binding, endPointAddr);
                  BindingElementCollection elements = client.Endpoint.Binding.CreateBindingElements();
                  elements.Find<SecurityBindingElement>().IncludeTimestamp = false;
                  client.Endpoint.Binding = new CustomBinding(elements);

                  // Ask the Add-In framework the handle the session logic
                  _globalContext.PrepareConnectSession(client.ChannelFactory);

                  // Go do what you need to do!
                  ClientInfoHeader cih = new ClientInfoHeader();
                  cih.AppID = "SAML Test";

                 // Initializing contact object
                  Contact contact = new Contact();
                  ID contactId = new ID();
                  contactId.id = 1762;
                  contactId.idSpecified = true;
                  contact.ID = contactId;
                  contact.CustomFields = new GenericObject{ };// to get custom fields

                  GetProcessingOptions gpo = new GetProcessingOptions();
                  gpo.FetchAllNames = true;

                  ClientInfoHeader clientInfoHeader = new ClientInfoHeader();
                  clientInfoHeader.AppID = "Basic Generic Read Sample";

                  RNObject[] results = client.Get(clientInfoHeader, new RNObject[] { contact }, gpo);

                  String customfields="";
                  foreach (Contact obj in results)
                  {
                      
                      GenericField[] genericFields = obj.CustomFields.GenericFields;
                      foreach (GenericField field in genericFields)
                      {
                          
                          Object[] Items = field.DataValue.Items;
                         
                           customfields += "CustomFields."+field.name+".*,";   // to get all package name under "CustomFields" like "C", "CO"

                          foreach (Object items in Items)
                          {
                              
                            MessageBox.Show("custom filed Field name: " + items.ToString()+" type :: "+items.GetType()); // unable to get fields under different packages of CustomFields like "C"
                          }
                          
                          
                      }

                  }
                  customfields = customfields.Remove(customfields.Length);

                  String queryString = "SELECT " + customfields + " FROM Contact limit 1"; // Query to get all fields under the different packages
                  String check = "";
                  byte[] byteArray;
                  CSVTableSet queryCSV = client.QueryCSV(cih, queryString, 100, ",", false, true, out byteArray);

                  CSVTable[] csvTables = queryCSV.CSVTables;
                  foreach(CSVTable table in csvTables)
                  {
                      String[] rowData = table.Columns.Split(',');
                      foreach (String data in rowData)
                      {
                          MessageBox.Show(data); // getting all custom fields
                 
                      }

                  }

      Appreciate your help..

    • Allan Schrum

      When you ask for the CustomFields in the Get request by creating an empty CustomFields object of type GenericObject, that is all that is required. The result will be an object with a CustomFields property of type GenericObject. That GenericObject will contain a list of GenericField objects where the name of each one will be the package names of all the custom fields and custom attributes in your system (e.g. "c", "CO", "RN"). Each GenericField is holding an ObjectValue of type GenericObject which is the wrapper for each of the individual fields under each package. That GenericObject "wrapper" object will contain an array of GenericField objects with each one representing the custom field you seek. The "Name" will be that of the custom field / custom attribute and the DataValue will be set to the type of data that is the custom field / custom attribute. Usually, this will be IntegerValue, or StringValue, or something similar. For some custom fields / custom attributes which reference other objects the GenericField will contains a NamedIDValue which represents the menu item or the foreign object reference associated with that custom field or custom attribute.

      You do not need to recursively descend into the CustomFields / GenericObject on your Get request to make this work.

      Regards,

      -Allan

    • Anurag Mittal

      Thanks Allan, it worked.

    • Davide Antonietti

      Anurag workaround has been a gorgeous tip.

      many thanx,

      Davide

    • Jose Antonio De La Rosa

      If I execute the following code in the basic example it goes right but if I execute the same code in a Add-ins WorkspaceRibbonAddIns I receive the error attached.

      Code:

                      GenericObject genericObjetc = new GenericObject();

       
                      ClientInfoHeader clientInfoHeader = new ClientInfoHeader();
                      clientInfoHeader.AppID = "Get Custom Object N3";
       
                      //Set the object type
                      RNObjectType objType = new RNObjectType();
                      objType.Namespace = "CO";
                      objType.TypeName = "N3";
                      genericObjetc.ObjectType = objType;
       
                      genericObjetc.ID = new ID();
                      genericObjetc.ID.id = 56;
                      genericObjetc.ID.idSpecified = true;
       
                      GetProcessingOptions getProcessingOptions = new GetProcessingOptions();
                      getProcessingOptions.FetchAllNames = false;
       
                      RNObject[] results = service.Get(clientInfoHeader, new RNObject[] { genericObjetc }, getProcessingOptions);
                      ...............
       
       
       
    • Raksh Pal

      Can any one let me know how to find the incident id on the basis of custom field. Please give an example i have tried but not able to do that.