8

I fetch data from Sharepoint using perl on a linuxmachine. This worked real good as long as I only had to read data from there.

Now that I also have to change data on Sharepoint I'm stuck.

Altough my JSON looked okay

{
  "__metadata": {
    "type": "SP.Data.DocLibItem"
  },
  "Title": "This is my testtitle"
}

all I got from Sharepoint was

{
  "error": {
    "code": "-1, Microsoft.SharePoint.Client.InvalidClientQueryException",
    "message": {
      "value": "Invalid JSON. A token was not recognized in the JSON content."
    }
  }
}

While trying to use different JSON-strings I found out that Sharepoint always throws this message. It doesn't matter if my JSON is valid or empty.

Is Sharepoint looking for that miraculous token before even parsing my JSON?

What am I missing here?

#!/usr/bin/perl

use strict;

use LWP::UserAgent;
use LWP::Authen::Ntlm;
use HTTP::Request::Common;
use JSON;
use Data::Dumper;

# config
  my $host = "mysite.local";
  my $user = 'domain\spadmin';
  my $pass = "iLikeMyBike";
  my $resource = "business/mytestsite";
  my $listid = "1bd8d1bc-e797-41ae-97c0-e3876406d0fe";
  my $itemindex = 20;

# globals
  my $digest;

# create useragent
  my $ua = LWP::UserAgent->new( keep_alive => 1);
  $ua->credentials("$host:443", "", $user, $pass);
  $ua->default_header('Accept' => "application/json;odata=verbose");
  $ua->timeout( 10 );

# get digest
  my $response = $ua->post( "https://$host/$resource/_api/contextinfo" );
  if ($response->is_success)
  {
    my $json = decode_json $response->decoded_content;
    $digest = $json->{d}->{GetContextWebInformation}->{FormDigestValue};
  }
  else
  {
    die $response->status_line;
  }

# post data
  my $msg = '{"__metadata":{"type":"SP.Data.DocLibItem"},"Title":"This is my testtitle"}';

  my $req = POST "https://$host/$resource/_api/lists('$listid')/items($itemindex)",
    "Content-type"     => "application/json;odata=verbose",
    "X-HTTP-Method"    => "MERGE",
    "X-RequestDigest"  => $digest,
    "IF-MATCH" => "*",
    "Content"  => { data => $msg };

  $response = $ua->request( $req );

  my $json = decode_json $response->decoded_content;
  print Dumper( $json );

  if ($response->is_success)
  {
    print "place to be :)\n";
  }
  else
  {
    die $response->status_line;
  }

Here are some http-dumps:

Without sending authorization again:

POST https://mysite.local/business/mytestsite/_api/lists('1bd8d1bc-e797-41ae-97c0-e3876406d0fe')/items(20)
Accept: application/json;odata=verbose
If-Match: *
User-Agent: libwww-perl/6.03
Content-Length: 206
Content-Type: application/json;odata=verbose
X-HTTP-Method: MERGE
X-RequestDigest: 0x0D1981C7E2CE0FDD...92309D60CBEB07B73F9F,19 May 2014 13:19:10 -0000

data=%7B%0A%09%22d%22%3A%0A%09%7B%0A%09%09%22__metadata%22%3A%0A%09%09%7B%0A%09%09%09%22type%22%3A+%22SP.Data.DocLibItem%22%0A%09%09%7D%2C%0A%09%09%22Title%22%3A+%22This+is+my+testtitle%22%0A%09%7D%0A%7D%0A

Sending authorization again (generates 3 posts):

post 1

POST https://mysite.local/business/mytestsite/_api/lists('1bd8d1bc-e797-41ae-97c0-e3876406d0fe')/items(20)
Accept: application/json;odata=verbose
If-Match: *
User-Agent: libwww-perl/6.03
Content-Length: 206
Content-Type: application/json;odata=verbose
X-HTTP-Method: MERGE
X-RequestDigest: 0x6292FA1483331806...2542815E3985B0306488,19 May 2014 13:22:58 -0000

data=%7B%0A%09%22d%22%3A%0A%09%7B%0A%09%09%22__metadata%22%3A%0A%09%09%7B%0A%09%09%09%22type%22%3A+%22SP.Data.DocLibItem%22%0A%09%09%7D%2C%0A%09%09%22Title%22%3A+%22This+is+my+testtitle%22%0A%09%7D%0A%7D%0A

post 2

POST https://mysite.local/business/mytestsite/_api/lists('1bd8d1bc-e797-41ae-97c0-e3876406d0fe')/items(20)
Accept: application/json;odata=verbose
Authorization: NTLM TlRTVNUAABAAAAB7ICAAgAAAABQAFACgAAABpY2hZ1pbnNpZ25h
If-Match: *
User-Agent: libwww-perl/6.03
Content-Length: 206
Content-Type: application/json;odata=verbose
X-HTTP-Method: MERGE
X-RequestDigest: 0x6292FA1483331806...2542815E3985B0306488,19 May 2014 13:22:58 -0000

data=%7B%0A%09%22d%22%3A%0A%09%7B%0A%09%09%22__metadata%22%3A%0A%09%09%7B%0A%09%09%09%22type%22%3A+%22SP.Data.DocLibItem%22%0A%09%09%7D%2C%0A%09%09%22Title%22%3A+%22This+is+my+testtitle%22%0A%09%7D%0A%7D%0A

post 3

POST https://mysite.local/business/mytestsite/_api/lists('1bd8d1bc-e797-41ae-97c0-e3876406d0fe')/items(20)
Accept: application/json;odata=verbose
Authorization: NTLM TlRMTVNTUAADAAA...E4AQQBpAGMAZQBbQBpAG4AaQBjAGUAYQBkAG0AaQBuAA==
If-Match: *
User-Agent: libwww-perl/6.03
Content-Length: 206
Content-Type: application/json;odata=verbose
X-HTTP-Method: MERGE
X-RequestDigest: 0x6292FA1483331806...2542815E3985B0306488,19 May 2014 13:22:58 -0000

data=%7B%0A%09%22d%22%3A%0A%09%7B%0A%09%09%22__metadata%22%3A%0A%09%09%7B%0A%09%09%09%22type%22%3A+%22SP.Data.DocLibItem%22%0A%09%09%7D%2C%0A%09%09%22Title%22%3A+%22This+is+my+testtitle%22%0A%09%7D%0A%7D%0A
  • Forgive my perl knowledge, but doesn't this overwrite the $msg? my $msg = '{"__metadata":{"type":"SP.Data.DocLibItem"},"Title":"This is my testtitle"}'; $msg = ""; – Robert Lindgren May 19 '14 at 10:44
  • Yes, it does. And it doesn't make a difference. The error gets thrown regardless of the contents of $msg. – tiMbeRdroP May 19 '14 at 11:45
  • I removed it to avoid confusion :) – tiMbeRdroP May 19 '14 at 11:45
  • I don't know Perl, so excuse me if I miss something. I don't see the content-length header, which is required for POST requests that send data in the request body. I don't see the Authorization or accept headers in the message, but I see them when you create $ua, so I'm assuming that handles them? – wjervis May 19 '14 at 11:57
  • The module does automatically create a content-length-header. accept is sent again by $ua. I didn't send Authorization again because it didn't make a difference and I assumed that the digest is enough. – tiMbeRdroP May 19 '14 at 13:47
3

Hmm

%7B%0A%09%22d%22%3A%0A%09%7B%0A%09%09%22__metadata%22%3A%0A%09%09%7B%0A%09%09%09%22type%22%3A+%22SP.Data.DocLibItem%22%0A%09%09%7D%2C%0A%09%09%22Title%22%3A+%22This+is+my+testtitle%22%0A%09%7D%0A%7D%0A

Decodes as a proper odata result

{
    "d":
    {
        "__metadata":
        {
            "type": "SP.Data.DocLibItem"
        },
        "Title": "This is my testtitle"
    }
}

but msdn specifies that the post data should be in a format similar to the below

{ '__metadata': { 'type': 'SP.Data.Team_x0020_tasksListItem' },
    'Title': 'Teams incorporate feedback', 'AssignedToId': {"results":[1]},
    'StartDate':'2014-01-29T08:00:00Z', 'DueDate':'2014-01-31T08:00:00Z',
    'PredecessorsId': {"results":[4]}}

So perhaps "d" is your unrecognized token?

Update:

Send a string that is not encoded

Change "Content" => { data => $msg }; to "Content" => $msg;

  • Sending %7B+'__metadata'%3A+%7B+%09%09'type'%3A'SP.Data.DocLibItem'+%7D%2C++'Title'%3A'This+is+my+testtitle'+%7D produces the same error. So does Mary+had+a+little+lamb. which makes me believe the problem is somewhere else :( – tiMbeRdroP May 20 '14 at 9:17
  • 1
    Have you tried sending an unencoded string? – Patrik Björklund May 20 '14 at 10:56
  • 1
    Wow! Changing "Content" => { data => $msg }; to just "Content" => $msg; did the trick. Never thought it would be as simple as that. Please create a separate answer which I can mark as accepted and post a full working example for future visitors. – tiMbeRdroP May 21 '14 at 9:03
  • 1
    Good to hear it working! I have no idea how perl handles encoding in it's libraries so at first I thought that you were supposed to send an encoded string like that and it would fix it somewhere down the line before sending the request. Assumption is the mother of all mistakes, right? – Patrik Björklund May 22 '14 at 8:22

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.