[SalesForce] Cannot deserialize instance of base64 from VALUE_STRING value

I have a class below where I am inserting a new merchandise object and also creating an attachment to be attached with this merchandise object(in notes and attachment object) using a http POST . I am running into a JSON PARSE ERROR which says: Cannot deserialize instance of base64 from VALUE_STRING value when I test the below JSON from the workbench:

      {
  "name" : "Pencil",
"id" : "a0e37000000lKep",
"attachs": [{
    "Body": "d29ybGQ=",
    "ContentType": "text/plain",
    "Name": "hello.txt"
}]
}

Apex Class which creates a Merchandise object and attachment object:

@RestResource(urlMapping='/Merchandise/*')
global with sharing class MerchandiseManager {


    @HttpGet
    global static Merchandise__c getMerchandiseById() {
        RestRequest req = RestContext.request;        
        String merchId = req.requestURI.substring(
                                  req.requestURI.lastIndexOf('/')+1);
        Merchandise__c result = 
                       [SELECT Name,Description__c,Price__c,Total_Inventory__c
                        FROM Merchandise__c 
                        WHERE Id = :merchId];
        return result;
    }

    @HttpPost
    global static String createMerchandise(String name,String id,List<Attachment> attachs) {
        Merchandise__c m = new Merchandise__c(
            Name=name,
            Warehouse__c=id);
        insert m;


    list<attachment> attachmentToInsert = new list<attachment>();

    for (Attachment att :attachs) {
        attachmentToInsert.add(
            new Attachment(parentId = m.Id, name = att.name, 
                ContentType = att.ContentType, Body = att.body));
    }
        return m.Id;
    }
}

What is going wrong here? How can I correct this to create a new merchandise object and attach the attachment to it?

Best Answer

I just tested serializing and deserializing an attachment in Salesforce, and JSON.serialize gave me something like this:

[{"attributes":
 {"type":"Attachment",
  "url":"/services/data/v37.0/sobjects/Attachment/00PXXXXXXXXXXXXXXX"},
  "Body":{
    "asByteArray":"AA==",
    "inputStream":{},
    "length":1,
    "maxToKeep":524288},
   "Id":"00PXXXXXXXXXXXXXXX"}
]

However, it wouldn't convert it back into an actual attachment, giving a similar error about not being able to handle base64 data.

This suggests to me that you're going to have to manually deserialize the values, perhaps like this:

@HttpPost
global static String createMerchandise(String name,String id,List<Wrapper> attachs) {
// Skipping prior code
Attachment[] attachments = new Attachment[0];
for(Wrapper item: attachs) {
    attachments.add(
        new attachment(
            name=item.name, 
            body=EncodingUtil.base64Decode(item.body), 
            ParentId=m.Id, 
            ContentType=item.mimeType
        ));
}
insert attachments;

You'll have to adjust your JSON to match. Wrapper is just a plain class:

class Wrapper {
    String name, body, mimeType;
}