8

I am trying to read a custom dependent picklist field and update it's field-dependencies. I have followed the common resources found online and getting the below error:

FATAL_ERROR System.CalloutException: Web service callout failed: Unable to find apex schema info

My MetadataService class snippet:

public class MetadataService {
   public class MetadataPort {
        /*other variables for session data here*/
        public MetadataService.IReadResult readMetadata(String type_x,String[] fullNames) {
            MetadataService.readMetadata_element request_x = new MetadataService.readMetadata_element();
            request_x.type_x = type_x;
            request_x.fullNames = fullNames;
            MetadataService.IReadResponseElement response_x;
            Map<String, MetadataService.IReadResponseElement> response_map_x = new Map<String, MetadataService.IReadResponseElement>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadata',
              'http://soap.sforce.com/2006/04/metadata',
              'readMetadataResponse',
              'MetadataService.read' + type_x + 'Response_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.getResult();
        }
   }
    public virtual class Metadata {
        public String fullName;
    }
    public class ReadCustomFieldResult implements IReadResult {
        public MetadataService.CustomField[] records;
        public MetadataService.Metadata[] getRecords() { return records; }
        private String[] records_type_info = new String[]{'records','http://soap.sforce.com/2006/04/metadata',null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }
    public class readCustomFieldResponse_element implements IReadResponseElement {
        public MetadataService.ReadCustomFieldResult result;
        public IReadResult getResult() { return result; }
        private String[] result_type_info = new String[]{'result','http://soap.sforce.com/2006/04/metadata',null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{'http://soap.sforce.com/2006/04/metadata','true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public interface IReadResult {
        MetadataService.Metadata[] getRecords();
    }
    public interface IReadResponseElement {
        IReadResult getResult();
    }
... 
/*Other dependant classes like CustomField, ValueSet etc here*/
}

Script that I am running to read the custom field:

MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = UserInfo.getSessionId();
service.readMetadata('CustomField', new String[]{'Account.State__c'});

The error is occurring at line:

WebServiceCallout.invoke(...);

I am guessing the response is not being parsed into readCustomFieldResponse_element class instance which is causing the error.

The salesforce documentation is also not very clear about the process. Any help would be greatly appreciated.

UPDATE 1: Following is the response XML I am receiving from readMetadata call, retrieved using SoapUI.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <readMetadataResponse>
         <result>
            <records xsi:type="CustomField">
               <fullName>Account.State__c</fullName>
               <externalId>false</externalId>
               <label>State Name</label>
               <required>false</required>
               <trackFeedHistory>false</trackFeedHistory>
               <trackHistory>false</trackHistory>
               <type>Picklist</type>
               <valueSet>
                  <controllingField>Country__c</controllingField>
                  <restricted>true</restricted>
                  <valueSetDefinition>
                     <sorted>false</sorted>
                     <value>
                        <fullName>Canberra</fullName>
                        <default>false</default>
                        <label>Canberra</label>
                     </value>
                     <value>
                        <fullName>Maharastra</fullName>
                        <default>false</default>
                        <label>Maharastra</label>
                     </value>
                     <value>
                        <fullName>Sidney</fullName>
                        <default>false</default>
                        <label>Sidney</label>
                     </value>
                     <value>
                        <fullName>West Bengal</fullName>
                        <default>true</default>
                        <label>West Bengal</label>
                     </value>
                  </valueSetDefinition>
                  <valueSettings>
                     <controllingFieldValue>India</controllingFieldValue>
                     <valueName>Maharastra</valueName>
                  </valueSettings>
               </valueSet>
            </records>
         </result>
      </readMetadataResponse>
   </soapenv:Body>
</soapenv:Envelope>

And my classes in MetadataService:

    public class ReadCustomFieldResult implements IReadResult {
        public MetadataService.CustomField[] records;
        public MetadataService.Metadata[] getRecords() { return records; }
        private String[] records_type_info = new String[]{'records',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }
    public class readCustomFieldResponse_element implements IReadResponseElement {
        public MetadataService.ReadCustomFieldResult result;
        public IReadResult getResult() { return result; }
        private String[] result_type_info = new String[]{'result',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }
    public class CustomField extends Metadata{
        public String fullName;

        public String businessOwnerGroup;
        public String businessOwnerUser;
        public String businessStatus;
        public Boolean caseSensitive;
        public String customDataType;
        public String defaultValue;
        public String deleteConstraint;
        public Boolean deprecated;
        public String description;
        public String displayFormat;
        public String encryptionScheme;
        public Boolean escapeMarkup;
        public String externalDeveloperName;
        public Boolean externalId;
        public String fieldManageability;
        public String formula;
        public String formulaTreatBlanksAs;
        public String inlineHelpText;
        public Boolean isAIPredictionField;
        public Boolean isConvertLeadDisabled;
        public Boolean isFilteringDisabled;
        public Boolean isNameField;
        public Boolean isSortingDisabled;
        public String label;
        public Integer length;
        public MetadataServiceBase.LookupFilter lookupFilter;
        public String maskChar;
        public String maskType;
        public String metadataRelationshipControllingField;
        public Boolean populateExistingRows;
        public Integer precision;
        public String referenceTargetField;
        public String referenceTo;
        public String relationshipLabel;
        public String relationshipName;
        public Integer relationshipOrder;
        public Boolean reparentableMasterDetail;
        public Boolean required;
        public Boolean restrictedAdminField;
        public Integer scale;
        public String securityClassification;
        public Integer startingNumber;
        public Boolean stripMarkup;
        public String summarizedField;
        public MetadataServiceBase.FilterItem[] summaryFilterItems;
        public String summaryForeignKey;
        public String summaryOperation;
        public Boolean trackFeedHistory;
        public Boolean trackHistory;
        public Boolean trackTrending;
        public String type_x;
        public Boolean unique;
        public MetadataServiceBase.ValueSet valueSet;
        public Integer visibleLines;
        public Boolean writeRequiresMasterRead;
        public Boolean displayLocationInDecimal;
        private String[] businessOwnerGroup_type_info = new String[]{'businessOwnerGroup',SOAP_M_URI,null,'0','1','false'};
        private String[] fullName_type_info = new String[]{'fullName',SOAP_M_URI,null,'0','1','false'};
        private String[] businessOwnerUser_type_info = new String[]{'businessOwnerUser',SOAP_M_URI,null,'0','1','false'};
        private String[] businessStatus_type_info = new String[]{'businessStatus',SOAP_M_URI,null,'0','1','false'};
        private String[] caseSensitive_type_info = new String[]{'caseSensitive',SOAP_M_URI,null,'0','1','false'};
        private String[] customDataType_type_info = new String[]{'customDataType',SOAP_M_URI,null,'0','1','false'};
        private String[] defaultValue_type_info = new String[]{'defaultValue',SOAP_M_URI,null,'0','1','false'};
        private String[] deleteConstraint_type_info = new String[]{'deleteConstraint',SOAP_M_URI,null,'0','1','false'};
        private String[] deprecated_type_info = new String[]{'deprecated',SOAP_M_URI,null,'0','1','false'};
        private String[] description_type_info = new String[]{'description',SOAP_M_URI,null,'0','1','false'};
        private String[] displayFormat_type_info = new String[]{'displayFormat',SOAP_M_URI,null,'0','1','false'};
        private String[] encryptionScheme_type_info = new String[]{'encryptionScheme',SOAP_M_URI,null,'0','1','false'};
        private String[] escapeMarkup_type_info = new String[]{'escapeMarkup',SOAP_M_URI,null,'0','1','false'};
        private String[] externalDeveloperName_type_info = new String[]{'externalDeveloperName',SOAP_M_URI,null,'0','1','false'};
        private String[] externalId_type_info = new String[]{'externalId',SOAP_M_URI,null,'0','1','false'};
        private String[] fieldManageability_type_info = new String[]{'fieldManageability',SOAP_M_URI,null,'0','1','false'};
        private String[] formula_type_info = new String[]{'formula',SOAP_M_URI,null,'0','1','false'};
        private String[] formulaTreatBlanksAs_type_info = new String[]{'formulaTreatBlanksAs',SOAP_M_URI,null,'0','1','false'};
        private String[] inlineHelpText_type_info = new String[]{'inlineHelpText',SOAP_M_URI,null,'0','1','false'};
        private String[] isAIPredictionField_type_info = new String[]{'isAIPredictionField',SOAP_M_URI,null,'0','1','false'};
        private String[] isConvertLeadDisabled_type_info = new String[]{'isConvertLeadDisabled',SOAP_M_URI,null,'0','1','false'};
        private String[] isFilteringDisabled_type_info = new String[]{'isFilteringDisabled',SOAP_M_URI,null,'0','1','false'};
        private String[] isNameField_type_info = new String[]{'isNameField',SOAP_M_URI,null,'0','1','false'};
        private String[] isSortingDisabled_type_info = new String[]{'isSortingDisabled',SOAP_M_URI,null,'0','1','false'};
        private String[] label_type_info = new String[]{'label',SOAP_M_URI,null,'0','1','false'};
        private String[] length_type_info = new String[]{'length',SOAP_M_URI,null,'0','1','false'};
        private String[] lookupFilter_type_info = new String[]{'lookupFilter',SOAP_M_URI,null,'0','1','false'};
        private String[] maskChar_type_info = new String[]{'maskChar',SOAP_M_URI,null,'0','1','false'};
        private String[] maskType_type_info = new String[]{'maskType',SOAP_M_URI,null,'0','1','false'};
        private String[] metadataRelationshipControllingField_type_info = new String[]{'metadataRelationshipControllingField',SOAP_M_URI,null,'0','1','false'};
        private String[] populateExistingRows_type_info = new String[]{'populateExistingRows',SOAP_M_URI,null,'0','1','false'};
        private String[] precision_type_info = new String[]{'precision',SOAP_M_URI,null,'0','1','false'};
        private String[] referenceTargetField_type_info = new String[]{'referenceTargetField',SOAP_M_URI,null,'0','1','false'};
        private String[] referenceTo_type_info = new String[]{'referenceTo',SOAP_M_URI,null,'0','1','false'};
        private String[] relationshipLabel_type_info = new String[]{'relationshipLabel',SOAP_M_URI,null,'0','1','false'};
        private String[] relationshipName_type_info = new String[]{'relationshipName',SOAP_M_URI,null,'0','1','false'};
        private String[] relationshipOrder_type_info = new String[]{'relationshipOrder',SOAP_M_URI,null,'0','1','false'};
        private String[] reparentableMasterDetail_type_info = new String[]{'reparentableMasterDetail',SOAP_M_URI,null,'0','1','false'};
        private String[] required_type_info = new String[]{'required',SOAP_M_URI,null,'0','1','false'};
        private String[] restrictedAdminField_type_info = new String[]{'restrictedAdminField',SOAP_M_URI,null,'0','1','false'};
        private String[] scale_type_info = new String[]{'scale',SOAP_M_URI,null,'0','1','false'};
        private String[] securityClassification_type_info = new String[]{'securityClassification',SOAP_M_URI,null,'0','1','false'};
        private String[] startingNumber_type_info = new String[]{'startingNumber',SOAP_M_URI,null,'0','1','false'};
        private String[] stripMarkup_type_info = new String[]{'stripMarkup',SOAP_M_URI,null,'0','1','false'};
        private String[] summarizedField_type_info = new String[]{'summarizedField',SOAP_M_URI,null,'0','1','false'};
        private String[] summaryFilterItems_type_info = new String[]{'summaryFilterItems',SOAP_M_URI,null,'0','-1','false'};
        private String[] summaryForeignKey_type_info = new String[]{'summaryForeignKey',SOAP_M_URI,null,'0','1','false'};
        private String[] summaryOperation_type_info = new String[]{'summaryOperation',SOAP_M_URI,null,'0','1','false'};
        private String[] trackFeedHistory_type_info = new String[]{'trackFeedHistory',SOAP_M_URI,null,'0','1','false'};
        private String[] trackHistory_type_info = new String[]{'trackHistory',SOAP_M_URI,null,'0','1','false'};
        private String[] trackTrending_type_info = new String[]{'trackTrending',SOAP_M_URI,null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type',SOAP_M_URI,null,'0','1','false'};
        private String[] unique_type_info = new String[]{'unique',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSet_type_info = new String[]{'valueSet',SOAP_M_URI,null,'0','1','false'};
        private String[] visibleLines_type_info = new String[]{'visibleLines',SOAP_M_URI,null,'0','1','false'};
        private String[] writeRequiresMasterRead_type_info = new String[]{'writeRequiresMasterRead',SOAP_M_URI,null,'0','1','false'};
        private String[] displayLocationInDecimal_type_info = new String[]{'displayLocationInDecimal',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'fullName','businessOwnerGroup','businessOwnerUser','businessStatus','caseSensitive','customDataType','defaultValue','deleteConstraint','deprecated','description','displayFormat','encryptionScheme','escapeMarkup','externalDeveloperName','externalId','fieldManageability','formula','formulaTreatBlanksAs','inlineHelpText','isAIPredictionField','isConvertLeadDisabled','isFilteringDisabled','isNameField','isSortingDisabled','label','length','lookupFilter','maskChar','maskType','metadataRelationshipControllingField','populateExistingRows','precision','referenceTargetField','referenceTo','relationshipLabel','relationshipName','relationshipOrder','reparentableMasterDetail','required','restrictedAdminField','scale','securityClassification','startingNumber','stripMarkup','summarizedField','summaryFilterItems','summaryForeignKey','summaryOperation','trackFeedHistory','trackHistory','trackTrending','type_x','unique','valueSet','visibleLines','writeRequiresMasterRead','displayLocationInDecimal'};
    }
    public virtual class Metadata {
        public String fullName;
    }
    public class ValueSet{
        public String controllingField;
        public Boolean restricted;
        public MetadataService.ValueSetValuesDefinition valueSetDefinition;
        public String valueSetName;
        public MetadataService.ValueSettings[] valueSettings;
        private String[] controllingField_type_info = new String[]{'controllingField',SOAP_M_URI,null,'0','1','false'};
        private String[] restricted_type_info = new String[]{'restricted',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSetDefinition_type_info = new String[]{'valueSetDefinition',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSetName_type_info = new String[]{'valueSetName',SOAP_M_URI,null,'0','1','false'};
        private String[] valueSettings_type_info = new String[]{'valueSettings',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'controllingField','restricted','valueSetDefinition','valueSetName','valueSettings'};
    }

public class ValueSetValuesDefinition {
        public Boolean sorted;
        public MetadataService.StandardValue[] value;
        private String[] sorted_type_info = new String[]{'sorted',SOAP_M_URI,null,'1','1','false'};
        private String[] value_type_info = new String[]{'value',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'sorted','value'};
    }

    public class StandardValue extends CustomValue {
        public String type = 'StandardValue';
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName',SOAP_M_URI,null,'0','1','false'};
        public String color;
        public Boolean default_x;
        public String description;
        public Boolean isActive;
        public String label;
        private String[] color_type_info = new String[]{'color',SOAP_M_URI,null,'0','1','false'};
        private String[] default_x_type_info = new String[]{'default',SOAP_M_URI,null,'1','1','false'};
        private String[] description_type_info = new String[]{'description',SOAP_M_URI,null,'0','1','false'};
        private String[] isActive_type_info = new String[]{'isActive',SOAP_M_URI,null,'0','1','false'};
        private String[] label_type_info = new String[]{'label',SOAP_M_URI,null,'0','1','false'};
        public Boolean allowEmail;
        public Boolean closed;
        public Boolean converted;
        public Boolean cssExposed;
        public String forecastCategory;
        public String groupingString;
        public Boolean highPriority;
        public Integer probability;
        public String reverseRole;
        public Boolean reviewed;
        public Boolean won;
        private String[] allowEmail_type_info = new String[]{'allowEmail',SOAP_M_URI,null,'0','1','false'};
        private String[] closed_type_info = new String[]{'closed',SOAP_M_URI,null,'0','1','false'};
        private String[] converted_type_info = new String[]{'converted',SOAP_M_URI,null,'0','1','false'};
        private String[] cssExposed_type_info = new String[]{'cssExposed',SOAP_M_URI,null,'0','1','false'};
        private String[] forecastCategory_type_info = new String[]{'forecastCategory',SOAP_M_URI,null,'0','1','false'};
        private String[] groupingString_type_info = new String[]{'groupingString',SOAP_M_URI,null,'0','1','false'};
        private String[] highPriority_type_info = new String[]{'highPriority',SOAP_M_URI,null,'0','1','false'};
        private String[] probability_type_info = new String[]{'probability',SOAP_M_URI,null,'0','1','false'};
        private String[] reverseRole_type_info = new String[]{'reverseRole',SOAP_M_URI,null,'0','1','false'};
        private String[] reviewed_type_info = new String[]{'reviewed',SOAP_M_URI,null,'0','1','false'};
        private String[] won_type_info = new String[]{'won',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] type_att_info = new String[]{'xsi:type'};
        private String[] field_order_type_info = new String[]{'fullName','color','default_x','description','isActive','label', 'allowEmail','closed','converted','cssExposed','forecastCategory','groupingString','highPriority','probability','reverseRole','reviewed','won'};
    }

Now we can see that all the elements returned in the response xml is present in the apex classes with corresponding propertyName_type_info variables. Now I am receiving

System.CalloutException: Web service callout failed: Unable to parse callout response. Apex type not found for element fullName

However, my apex classes have fullName property as well as its type_info variable as well. Any idea what's causing this issue?

  • 2
    You can use Salesforce UI api to get picklist values, might help you than parsing Soap response. developer.salesforce.com/docs/atlas.en-us.uiapi.meta/uiapi/… – Pranay Jaiswal Nov 27 '18 at 19:32
  • if not UI api, you can use metadata rest API: '/services/data/v20.0/sobjects/Account/describe/' – Pranay Jaiswal Nov 27 '18 at 20:12
  • Thanks Pranay, however the requirement is the update the picklist dependencies. So our approach is to fetch the picklist first and then update it using metadata api. – Santanu Halder Nov 29 '18 at 6:55
10
+50

This is a very similar scenario to what is occurring in Tooling API in Apex - polymorphism, generic query() callout? and is caused by an underlying limitation in Wsdl2Apex and WebServiceCallout.invoke().

The Metadata API readMetadata web method returns a generic ReadResult with records of type tns:Metadata:

<xsd:complexType name="ReadResult">
 <xsd:sequence>
  <xsd:element name="records" minOccurs="0" maxOccurs="unbounded" type="tns:Metadata"/>
 </xsd:sequence>
</xsd:complexType>

Which is fine, except in this case you don't get tns:Metadata records back. You get tns:CustomField records back that are an extension of tns:Metadata and include a sequence of elements specific to custom fields.

WebServiceCallout.invoke doesn't know how to unpack the specific CustomField complex type when it was just expecting Metadata.

Instead, you need to create a classes to do this and then utilize them in a dedicated version of the readMetadata method.

It looks like you are using the apex-mdapi implementation here for readMetadata. This attempts to overcome the limitation above by passing in target metadata type with the type_x parameter and then adjusting the final infoArray parameter on WebServiceCallout.invoke to have a response type that matches what will come back from the WebService.

This will be a readCustomFieldResponse_element in your case and the corresponding ReadCustomFieldResult.

This is the point where I believe the issue is. Your CustomField needs to match that of your current API version. Each element that comes back needs to have a corresponding propertyName_type_info.

  • Thanks Daniel. The detailed explanation helped. I found out that my CustomField class had few mismatches with the current version of that. I have taken care of that and now CustomField class is up-to-date and I am receiving a new error. I have added the details in the question. This seems I am missing something obvious. – Santanu Halder Nov 28 '18 at 11:37
  • 2
    @SantanuHalder I wonder if the problem is on the virtual class Metadata implementation that has fullName but no corresponding type_info. – Daniel Ballinger Nov 28 '18 at 21:40
  • 1
    I was able to work it out with the hint given by you. Thank you very much Daniel. – Santanu Halder Nov 29 '18 at 7:12
3

Taken from Issue # 225 on their repo. Error occurred when obtaining a custom picklist fields CustomValue.

Error:

System.CalloutException: Web service callout failed: Unable to find apex schema info

Fresh install looked like this:

public virtual class CustomValue extends Metadata {
    public String color;
    public Boolean default_x;
    public String description;
    public Boolean isActive;
    public String label;
 }

Replacement code should look something like this (you can edit it as needed):

public virtual class CustomValue extends Metadata {
    public String type = 'CustomValue';
    public String fullName;
    private String[] fullName_type_info = new String[]{'fullName',API_END,null,'0','1','false'};
    public String color;
    public Boolean default_x;
    public String description;
    public Boolean isActive;
    public String label;
    private String[] color_type_info = new String[]{'color',API_END,null,'0','1','false'};
    private String[] default_x_type_info = new String[]{'default',API_END,null,'1','1','false'};
    private String[] description_type_info = new String[]{'description',API_END,null,'0','1','false'};
    private String[] isActive_type_info = new String[]{'isActive',API_END,null,'0','1','false'};
    private String[] label_type_info = new String[]{'label',API_END,null,'0','1','false'};
    private String[] apex_schema_type_info = new String[]{API_END,'true','false'};
    private String[] field_order_type_info = new String[]{'fullName','color','default_x','description','isActive','label'};
 }

Steps:

  1. Replace the class CustomValue with the above code.
  2. Replace API_END with SOAP_M_URI

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.