So, for the Intuit IPP Rest API,
Say if i want to query a customer who's name is ABC, i can use a http get request like this
https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'ABC'
&3D is a url escape of '=', this works without any problem.
Now if the customer's name is A&B, I tried the query string like this
select Id from Customer where FullyQualifiedName='A&B'
After the url encoding, it looks like this
https://qb.sbfinance.intuit.com/v3/company/198445012/query?query=select Id from Customer where FullyQualifiedName%3D'A%26B'
It will fail.
Any Idea?
Update
The above urls i copied from the IPP's API explorer.
Here is the code, I am using DevDefined.OAuth
IConsumerRequest conReq = _oSession.Request();
conReq = conReq.Get();
conReq.AcceptsType = "application/xml";
//conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='ABC'")); if use this line, it works fine
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='A&B'"));
try
{
string str = conReq.ReadBody();
catch (Exception ex)
{
//ex.Message
}
the returned xml data like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T06:24:12.408-07:00">
<Fault type="ValidationFault">
<Error code="4000">
<Message>Error parsing query</Message>
<Detail>QueryParserError: Invalid content. Lexical error at line 1, column 44. Encountered: <EOF> after : "\'A"</Detail>
</Error>
</Fault>
</IntuitResponse>
I am not 100% sure, yesterday when i test, it actually return something says the oauth failed. But this is what I got this morning.
Actually, you can try it within IPP's API explorer, it gives the same result.
The devdefined's code for ForUrl
public static IConsumerRequest ForUrl(this IConsumerRequest request, string url)
{
request.Context.RawUri = new Uri(url);
return request;
}
That will encode the url as
https://qb.sbfinance.intuit.com/v3/company/1122502005/query?query=select%20*%20from%20Customer%20where%20DisplayName='A&B'
Ok, finally, found the issue:
The real issue is Uri(url) won't escape the & in 'A&B' because it doesn't know if it is a url & or part of the data, So i changed the following line
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, #"select * from Customer where DisplayName='A&B'"));
as
conReq = conReq.ForUrl(string.Format(#"https://qb.sbfinance.intuit.com/v3/company/{0}/query?query={1}", Settings.Default.QuickBooksOnlineRealmId, Uri.EscapeDataString(#"select * from Customer where DisplayName='A&B'")));
use Uri.EscapeDataString to escape the data query string first.
If you create a customer with name 'A&B', then V3 service returns that customer object like below -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" time="2014-03-20T01:54:46.834-07:00">
<Customer domain="QBO" sparse="false">
<Id>10</Id>
<SyncToken>0</SyncToken>
<MetaData>
<CreateTime>2014-03-20T01:54:46-07:00</CreateTime>
<LastUpdatedTime>2014-03-20T01:54:47-07:00</LastUpdatedTime>
</MetaData>
<FullyQualifiedName>A&B</FullyQualifiedName>
<DisplayName>A&B</DisplayName>
<PrintOnCheckName>A&B</PrintOnCheckName>
<Active>true</Active>
<Taxable>true</Taxable>
<Job>false</Job>
<BillWithParent>false</BillWithParent>
<Balance>100.00</Balance>
<BalanceWithJobs>100.00</BalanceWithJobs>
<PreferredDeliveryMethod>Email</PreferredDeliveryMethod>
</Customer>
</IntuitResponse>
To retrieve this object by name, you need to use the following query
SELECT * FROM Customer WHERE DisplayName = 'A&B'
But it needs to be url encoded like following
SELECT+*+FROM+Customer+WHERE+DisplayName+%3D+%27A%26B%27
Java code to achieve this -
Customer customer = GenerateQuery.createQueryEntity(Customer.class);
String query = select($(customer)).where($(customer.getDisplayName()).eq("A&B")).generate();
// Query output - SELECT * FROM Customer WHERE DisplayName = 'A&B'
String encodedUrl = URLEncoder.encode(query, "UTF-8");
It works perfectly. (Devkit handles all these pretty well )
Hope this answers your qts.
Thanks
Please see an example here to escape special chars-
using Intuit.Ipp.Core;
using Intuit.Ipp.Data;
using Intuit.Ipp.LinqExtender;
using Intuit.Ipp.QueryFilter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
static class SampleCalls
{
public static Customer QueryCustomerByDisplayName(ServiceContext context, string displayName) {
displayName = displayName.Replace("'", "\\'"); //Escape special characters
QueryService<Customer> customerQueryService = new QueryService<Customer>(context);
return customerQueryService.Where(m => m.DisplayName == displayName).FirstOrDefault();
}
}
Related
I'm making an XML API call in Postman and I get the following response:
<response>
<result>
<system>YYY:XXXXXXXXX</system>
</result>
</response>
I created a variable from the content in "system", but I need to remove the "YYY:" from the front. Here is how I created the variable:
var response = xml2Json(responseBody);
var uuid = response["response"]["result"]["system"];
pm.collectionVariables.set("uuid", uuid);
If that could be formatted to remove any possible other characters before the colon - ex: "CCC:" or "ABC:" or "VAR1:" - that would be a bonus, but hard-coding this to remove exactly "YYY:" would solve the current problem.
You could do this, split String by character ":"
let text = "YYY:XXXXXXXXX";
console.log(text.split(":")[1]);
Apply to your code:
var response = xml2Json(responseBody);
var uuid = response["response"]["result"]["system"];
pm.collectionVariables.set("uuid", uuid.split(":")[1]);
Result:
I'd like to edit custom employee data in Workday using their API but the actual custom data format is not specified in the documentation. Also, I was not able to find a way to retrieve additional worker data. Google doesn't find any examples of using their API for this (Edit_Worker_Additional_Data function).
This is how the SOAP request would look like, including all optional parameters.
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<env:Body>
<wd:Edit_Worker_Additional_Data_Request
xmlns:wd="urn:com.workday/bsvc"
wd:version="v28.0">
<wd:Business_Process_Parameters>
<wd:Auto_Complete>true</wd:Auto_Complete>
<wd:Run_Now>true</wd:Run_Now>
<wd:Comment_Data>
<wd:Comment>abcdef</wd:Comment>
<wd:Worker_Reference>
<wd:ID wd:type="Contingent_Worker_ID">abcdef</wd:ID>
</wd:Worker_Reference>
</wd:Comment_Data>
</wd:Business_Process_Parameters>
<wd:Worker_Custom_Object_Data>
<wd:Effective_Date>2017-07-20</wd:Effective_Date>
<wd:Worker_Reference>
<wd:ID wd:type="Contingent_Worker_ID">abcdef</wd:ID>
</wd:Worker_Reference>
<wd:Business_Object_Additional_Data></wd:Business_Object_Additional_Data>
</wd:Worker_Custom_Object_Data>
</wd:Edit_Worker_Additional_Data_Request>
</env:Body>
</env:Envelope>
You have to define the Custom Object (or Additional Data) elements within
<wd:Business_Object_Additional_Data></wd:Business_Object_Additional_Data>
If your Custom Object is defined as "TestObject" for example, you would need both the Object and Field Reference IDs, and it would look like this:
<wd:Business_Object_Additional_Data>
<cus:TestObject>
<cus:TestObjectField>Value</cus:TestObjectField>
</cus:TestObject>
</wd:Business_Object_Additional_Data>
If you are using Java, here is an example usage of the Workday API to update worker data. This is not the Edit_Worker_Additional_Data, but they all work very similar and maybe this code snippet will help someone get started. All of the Java classes here are generated from the wsdl using jaxws-maven-plugin, except for WorkdayCredentials class, see Workday Soap API - User Name/Password for that.
public void updateWorkerContactInfo(Worker worker) throws WorkerDataException,
WorkerDataInvalidException {
HumanResourcesPort hrPort = hrService.getHumanResources();
BindingProvider bp = (BindingProvider) hrPort;
WorkdayCredentials.addWorkdayCredentials(bp,
config.getWdIntegrationUsername(),
config.getWdIntegrationPassword());
MaintainContactInformationForPersonEventRequestType body
= new MaintainContactInformationForPersonEventRequestType();
body.setAddOnly(false);
BusinessProcessParametersType params = new BusinessProcessParametersType();
params.setAutoComplete(false);
params.setRunNow(false);
body.setBusinessProcessParameters(params);
ContactInformationForPersonEventDataType contactData
= new ContactInformationForPersonEventDataType();
edu.bowdoin.workdayservice.data.hr.WorkerObjectType workerObjectType
= new edu.bowdoin.workdayservice.data.hr.WorkerObjectType();
edu.bowdoin.workdayservice.data.hr.WorkerObjectIDType idType
= new edu.bowdoin.workdayservice.data.hr.WorkerObjectIDType();
idType.setType("Employee_ID");
idType.setValue(worker.getWorkerId());
workerObjectType.getID().add(idType);
contactData.setWorkerReference(workerObjectType);
Date effectiveDate = new Date();
// set the effective date to the hire date + 1 day, this has to be
// greater than any other change to the worker address data, for
// example during the new hire process
if (worker.getHireDate() != null) {
DateTime hireDate = new DateTime(worker.getHireDate());
DateTime hireDatePlus1Day = hireDate.plusDays(1);
DateTime today = DateTime.now();
// only use hire date plus 1 if it is after today's date
if (hireDatePlus1Day.isAfter(today)) {
effectiveDate = hireDatePlus1Day.toDate();
}
}
contactData.setEffectiveDate(dateToXMLGregorian(effectiveDate));
ContactInformationDataType contactDataType
= new ContactInformationDataType();
EmailAddressInformationDataType emailAddressDataType
= new EmailAddressInformationDataType();
emailAddressDataType.setEmailAddress(worker.getPrimaryWorkEmail());
CommunicationMethodUsageInformationDataType usageDataType
= new CommunicationMethodUsageInformationDataType();
usageDataType.setPublic(true);
CommunicationUsageTypeDataType usageTypeData
= new CommunicationUsageTypeDataType();
usageTypeData.setPrimary(true);
CommunicationUsageTypeObjectType usageTypeObjectType
= new CommunicationUsageTypeObjectType();
CommunicationUsageTypeObjectIDType usageTypeObjectID
= new CommunicationUsageTypeObjectIDType();
usageTypeObjectID.setType("Communication_Usage_Type_ID");
usageTypeObjectID.setValue("WORK");
usageTypeObjectType.getID().add(usageTypeObjectID);
usageTypeData.setTypeReference(usageTypeObjectType);
usageDataType.getTypeData().add(usageTypeData);
emailAddressDataType.getUsageData().add(usageDataType);
contactDataType.getEmailAddressData().add(emailAddressDataType);
contactData.setWorkerContactInformationData(contactDataType);
body.setMaintainContactInformationData(contactData);
try {
hrPort.maintainContactInformation(body);
} catch (edu.bowdoin.workdayservice.data.hr.ProcessingFaultMsg e) {
throw new WorkerDataException(e.getMessage(), e);
} catch (edu.bowdoin.workdayservice.data.hr.ValidationFaultMsg e) {
throw new WorkerDataInvalidException(e.getMessage(), e);
} finally {
}
}
I have a REST Controller as follows using Spring boot 1.3.5 and Java 8. This works perfect while testing with Advanced REST Client (ARC) Utility - send xml request and returns the expected xml response as shown right below this controller code.
#RestController
#EnableAutoConfiguration
#RequestMapping("/rateQuote")
public class RateQuoteController {
#Autowired
private RateQuotingService rateQuotingService;
#RequestMapping(method = RequestMethod.POST, consumes = {"application/json","application/xml"}, produces = {"application/json","application/xml"})
public ResponseEntity<RateQuoteDto> getRateQuote(#RequestBody RESTRequestDto request){
RateQuoteDto rateQuoteDto = rateQuotingService.getRateQuote(request.getStateCode(), request.getZipCode(), request.getClient(), request.getThreshold(),
benefit,request.getLob(), localEffectiveDate);
return (new ResponseEntity<>(rateQuoteDto, HttpStatus.OK));
}
}
Request with ARC-
url - http://localhost:8080/rateQuote
Method - POST
Headers -
Content-Type: application/xml
Accept: application/xml
Request Body -
<request>
<stateCode>NY</stateCode>
<zipCode>10005</zipCode>
<client>BMG</client>
<threshold>1000</threshold>
<lob>PDI</lob>
<benefitLevel>15000</benefitLevel>
<effectiveDate>2016-01-01</effectiveDate>
</request
Response -
<?xml version="1.0" encoding="UTF-8"?>
<rateQuote>
<status>Approve</status>
<message>Quote is successfully retrieved.</message>
<quote>
<threshold1 amount="1000">
<benefits>
<benefitLevel amount="15000">
<annualPremium>990.00</annualPremium>
<semiAnnualPremium>440.00</semiAnnualPremium>
<quaterlyPremium>200.00</quaterlyPremium>
<monthlyPremium>77.00</monthlyPremium>
</benefitLevel>
</benefits>
</threshold1>
</quote>
</rateQuote>
So far so good. However, I have an issue with response when call is made grammatically.And the issue is that the most of the response's nodes are null as shown below:
#Test
public void getQuote()throws Exception{
RestTemplate template = new RestTemplate();
//RESTRequestDto is JAXB decorated
RESTRequestDto body = new RESTRequestDto();
body.setStateCode("NY");
body.setZipCode("10005");
body.setClient("BMG");
body.setThreshold("1000");
body.setLob("PDI");
body.setBenefitLevel("15000");
body.setEffectiveDate(DateUtils.createDate("2016-01-01", "yyyy-MM-dd"));
RequestEntity<RESTRequestDto> request = RequestEntity.post(new URI("http://localhost:8080/rateQuote"))
.accept(MediaType.APPLICATION_XML).contentType(MediaType.APPLICATION_XML).body(body);
ResponseEntity<RateQuoteDto> response = template.exchange(request, RateQuoteDto.class);
RateQuoteDto rateQuote = response.getBody();
System.out.println(rateQuote);
}
Response -
RateQuoteDto{status=Approve, message=Quote is successfully retrieved., zipCode=null, clientId=null, quote=QuoteDto{threshold1=ThresholdDto{amount=1000, benefitLevelLst=null}}}
However, changing the Media Type to JSON works as shown below:
RequestEntity<RESTRequestDto> request = RequestEntity.post(new URI("http://localhost:8080/rateQuote"))
.accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON).body(body);
Response -
RateQuoteDto{status=Approve, message=Quote is successfully retrieved., zipCode=10005, clientId=BMG, quote=QuoteDto{threshold1=ThresholdDto{amount=1000, benefitLevelLst=[BenefitDto{amount=15000, annualPremium=990.00, semiAnnualPremium=440.00, quaterlyPremium=200.00, monthlyPremium=77.00}]}}}
Observation -
As shown below, the ist benefitLevelLst is not bound with MediaType.XML returning null value show above but works just fine with MediaType.JSON. Any idea as to why?
public class ThresholdDto {
#XmlAttribute
private String amount;
#XmlElementWrapper(name = "benefits")
#XmlElement(name = "benefitLevel")
private List<BenefitDto> benefitLevelLst;
. . .
}
I saw this question already, but I didnt see an answer..
So I get this error:
The ':' character, hexadecimal value 0x3A, cannot be included in a name.
On this code:
XDocument XMLFeed = XDocument.Load("http://feeds.foxnews.com/foxnews/most-popular?format=xml");
XNamespace content = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = feed.Element("title").Value,
Link = feed.Element("link").Value,
pubDate = feed.Element("pubDate").Value,
Description = feed.Element("description").Value,
MediaContent = feed.Element(content + "encoded")
};
foreach (var f in feeds.Reverse())
{
....
}
An item looks like that:
<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/" />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink="false">http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src="http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc" height="1" width="1"/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&#160;</description>
<dc:date xmlns:dc="http://purl.org/dc/elements/1.1/">2012-04-4T19:44:51Z</dc:date>
<feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink>
</item>
....items....
</channel>
</rss>
All I want is to get the "http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg", and before that check if content:encoded exists..
Thanks.
EDIT:
I've found a sample that I can show and edit the code that tries to handle it..
EDIT2:
I've done it in the ugly way:
text.Replace("content:encoded", "contentt").Replace("xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"","");
and then get the element in the normal way:
MediaContent = feed.Element("contentt").Value
The following code
static void Main(string[] args)
{
var XMLFeed = XDocument.Parse(
#"<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc='http://purl.org/dc/elements/1.1/' />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink='false'>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content='http://purl.org/rss/1.0/modules/content/'><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src='http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc' height='1' width='1'/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&#160;</description>
<dc:date xmlns:dc='http://purl.org/dc/elements/1.1/'>2012-04-4T19:44:51Z</dc:date>
<!-- <feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink> -->
</item>
....items....
</channel>
</rss>");
XNamespace contentNs = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = (string)feed.Element("title"),
Link = (string)feed.Element("link"),
pubDate = (string)feed.Element("pubDate"),
Description = (string)feed.Element("description"),
MediaContent = GetMediaContent((string)feed.Element(contentNs + "encoded"))
};
foreach(var item in feeds)
{
Console.WriteLine(item);
}
}
private static string GetMediaContent(string content)
{
int imgStartPos = content.IndexOf("<img");
if(imgStartPos > 0)
{
int startPos = content[0] == '|' ? 1 : 0;
return content.Substring(startPos, imgStartPos - startPos);
}
return string.Empty;
}
results in:
{ Title = Pentagon confirms plan to create new spy agency, Link = http://feeds.f
oxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/, pubDate = Tue, 24 Apr 2012 1
2:44:51 PDT, Description = The Pentagon confirmed Tuesday that it is carving out
a brand new spy agency expected to include several hundred officers focused on
intelligence gathering around the world. , MediaContent = http://global
.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg }
Press any key to continue . . .
A few points:
You never want to treat Xml as text - in your case you removed the namespace declaration but actually if the namespace was declared inline (i.e. without binding to the prefix) or a different prefix would be defined your code would not work even though semantically both documents would be equivalent
Unless you know what's inside CDATA and how to treat it you always want to treat is as text. If you know it's something else you can treat it differently after parsing - see my elaborate on CDATA below for more details
To avoid NullReferenceExceptions if the element is missing I used explicit conversion operator (string) instead of invoking .Value
the Xml you posted was not a valid xml - there was missing namespace Uri for feedburner prefix
This is no longer related to the problem but may be helpful for some folks so I am leaving it
As far as the contents of the encode element is considered it is inside CDATA section. What's inside CDATA section is not an Xml but plain text. CDATA is usually used to not have to encode '<', '>', '&' characters (without CDATA they would have to be encoded as < > and & to not break the Xml document itself) but the Xml processor treat characters in the CDATA as if they were encoded (or to be more correct in encodes them). The CDATA is convenient if you want to embed html because textually the embedded content looks like the original yet it won't break your xml if the html is not a well-formed Xml. Since the CDATA content is not an Xml but text it is not possible to treat it as Xml. You will probably need to treat is as text and use for instance regular expressions. If you know it is a valid Xml you can load the contents to an XElement again and process it. In your case you have got mixed content so it is not easy to do unless you use a little dirty hack. Everything would be easy if you have just one top level element instead of mixed content. The hack is to add the element to avoid all the hassle. Inside the foreach look you can do something like this:
var mediaContentXml = XElement.Parse("<content>" + (string)item.MediaContent + "</content>");
Console.WriteLine((string)mediaContentXml.Element("img").Attribute("src"));
Again it's not pretty and it is a hack but it will work if the content of the encoded element is valid Xml. The more correct way of doing this is to us XmlReader with ConformanceLevel set to Fragment and recognize all kinds of nodes appropriately to create a corresponding Linq to Xml node.
You should use XNamespace:
XNamespace content = "...";
// later in your code ...
MediaContent = feed.Element(content + "encoded")
See more details here.
(Of course, you the string to be assigned to content is the same as in xmlns:content="...").
I have a very simple WCF service running which returns the following (from a basic new project) xml:
<ArrayOfSampleItem xmlns="http://schemas.datacontract.org/2004/07/WcfRestService1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<SampleItem>
<Id>1</Id>
<StringValue>Hello</StringValue>
</SampleItem>
</ArrayOfSampleItem>
I am then consuming this in a Windows Phone 7 app. The result is coming back fine however I'm having problems parsing the xml. This is the code I am using on the callback after completion of the request:
XDocument xmlDoc = XDocument.Parse(e.Result);
itemsFetched.ItemsSource = from item in xmlDoc.Descendants("SampleItem")
select new Product()
{
Id = item.Element("Id").Value,
StringValue = item.Element("StringValue").Value
};
The collection is not populated with this, when I try adding the namespace:
XNamespace web = "http://schemas.datacontract.org/2004/07/WcfRestService1";
XDocument xmlDoc = XDocument.Parse(e.Result);
itemsFetched.ItemsSource = from item in xmlDoc.Descendants(web + "SampleItem")
The item is found but I get a null exception when it attempts to get the Id value.
Any help would be much appreciated.
Well the xmlns="..." puts the elements and all its descendants in the namespace so you need to use your XNamespace object web anywhere where you access elements:
XDocument xmlDoc = XDocument.Parse(e.Result);
XNamespace web = "http://schemas.datacontract.org/2004/07/WcfRestService1";
itemsFetched.ItemsSource = from item in xmlDoc.Descendants(web + "SampleItem")
select new Product()
{
Id = item.Element(web + "Id").Value,
StringValue = item.Element(web + "StringValue").Value
};