Parsing malformed XML with RestSharp - c#

I have a webservice that I need to consume that's returning the following:
<catalog modules="2" children="0">
<title>Test Catalog</title>
<link type="application/xml" rel="self" href="http://someurl"/>
<link type="text/html" rel="alternate" href="http://someurl"/>
<parent modules="0" children="3">
<title>Top</title>
<link type="application/xml" rel="self" href="http://someurl"/>
<link type="text/html" rel="alternate" href="http://someurl"/>
</parent>
<module>
<id>MODULEID123</id>
<title>Some module title</title>
<link type="application/xml" rel="self" href="http://someurl"/>
<link type="text/html" rel="alternate" href="http://someurl"/>
<type>type123</type>
<description>Some Description</description>
</module>
<module>
<id>MODULEID456</id>
<title>Some other module title</title>
<link type="application/xml" rel="self" href="http://someurl"/>
<link type="text/html" rel="alternate" href="http://someurl"/>
<type>type123</type>
<description/>
</module>
</catalog>
I'm using RestSharp to consume the service, and under normal circumstances I'd expect the tags to be underneath a parent node or something similar so that I could just a response class with a List<Module> Modules that would just automatically pull them in. However, since they're just out there equal to the <parent>, <title> and <link> nodes, it looks almost malformed (though, admittedly, it's been a long time since I've been deep into how XML *should * look - thank you, JSON!
So, given that this is the returned result, how can I direct RestSharp to parse this? If RestSharp expects well formed XML, and thus rejects this, should I just manually parse this using an XMLReader the old fashioned way?

Oops, found it in the documentation. Evidently this is convention-based, so while this looks a bit wonky, it should be no problem if I just assume there's a parent there build my response class correctly.
From the docs...
XmlDeserializer
Two different types of lists are handled: parentless (inline) and regular (nested). For instance, both of the following XML structures
<?xml version="1.0" encoding="utf-8" ?>
<InlineListSample>
<image src="1.gif">value1</image>
<image src="2.gif">value2</image>
<image src="3.gif">value3</image>
<image src="4.gif">value4</image>
</InlineListSample>
<?xml version="1.0" encoding="utf-8" ?>
<NestedListSample>
<images>
<image src="1.gif">value1</image>
<image src="2.gif">value2</image>
<image src="3.gif">value3</image>
<image src="4.gif">value4</image>
</images>
</NestedListSample>
Will map to the following C# schema:
public class ListSample
{
public List<Image> Images { get; set; }
}
public class Image
{
public string Src { get; set; }
public string Value { get; set; }
}
If by chance both element structures existed in the same document at the relevant point in the hierarchy, parented/nested/regular lists take priority.

Related

Rest/Odata $Filter not working on certain keys

I am trying to query some information from a SharePoint list. Now, I am trying to use odata to get it. my problem is that for some reason, $filter is not working with all my keys in that list :S. Here is an example:
This works OK:
...ccm/_api/Lists/GetByTitle('Circuit%20Deliveries')/Items?$filter=Title eq 'AUG'
But this one doesn't work (HTTP 400 bad request)
...ccm/_api/Lists/GetByTitle('Circuit%20Deliveries')/Items?$filter=Delivery_x0020_Date eq null
Nevertheless, the field name is OK, since I am able to use select and orderby with the same field name. Here is the XML output in IE for 2 'Titles' named AUG:
...ccm/_api/Lists/GetByTitle('Circuit%20Deliveries')/Items?$filter=Title
eq
'AUG'&$select=Title,Delivery_x0020_Date,Status_x0020__x002d__x0020_Calcu
<?xml version="1.0" encoding="utf-8" ?> -
<feed xml:base="/ccm/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss"
xmlns:gml="http://www.opengis.net/gml">
<id>5a2630a8-c00a-4baa-bd43-3912d9264df8</id>
<title />
<updated>2017-09-08T10:58:12Z</updated>
<entry m:etag="" 33 "">
<id>Web/Lists(guid'5ad8ca39-191a-4586-bdef-7bc4830eced9')/Items(332)</id>
<category term="SP.Data.Circuit_x0020_DeliveriesListItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'5ad8ca39-191a-4586-bdef-7bc4830eced9')/Items(332)" />
<title />
<updated>2017-09-08T10:58:12Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:Title>AUG</d:Title>
<d:Delivery_x0020_Date m:type="Edm.DateTime">2017-03-06T06:00:00Z</d:Delivery_x0020_Date>
<d:Status_x0020__x002d__x0020_Calcu>Circuit Ready for Production Use on 03/06/2017</d:Status_x0020__x002d__x0020_Calcu>
</m:properties>
</content>
</entry>
<entry m:etag="" 58 "">
<id>Web/Lists(guid'5ad8ca39-191a-4586-bdef-7bc4830eced9')/Items(333)</id>
<category term="SP.Data.Circuit_x0020_DeliveriesListItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'5ad8ca39-191a-4586-bdef-7bc4830eced9')/Items(333)" />
<title />
<updated>2017-09-08T10:58:12Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:Title>AUG</d:Title>
<d:Delivery_x0020_Date m:null="true" />
<d:Status_x0020__x002d__x0020_Calcu>Site Survey completed on 09/09/2016</d:Status_x0020__x002d__x0020_Calcu>
</m:properties>
</content>
</entry>
</feed>
As you can see I was able to successfully query and select both fields from the list and filter by Title. Nevertheless, I would like to further filter on Delivery_x0020_Date being empty (or null).
I searched around and some people were saying that filtering by null on dates is not always working. But in my case is a bit more than that, because even if I try to filter by another key(Status_x0020__x002d__x0020_Calcu) it will return Bad Request
Is there a requirement on what type of keys you can filter on?
Thank you
REST API in SharePoint 2013 does Not support null values for filtering on list item queries. (Refer this MSDN link)
You will have to find some other workaround.
however, We can combine the CAML query with REST API to get the required data in Javascript object model. Refer this link - REST API to filter on null.
But I am not sure about the c# code.

How to use LINQ to get data from an XML file?

I have xml files which look like this:
<?xml version="1.0" encoding="utf-8"?>
<record id="177" restricted="false">
<type>record type</type>
<startdate>2000-10-10</startdate>
<enddate>2014-02-01</enddate>
<titles>
<title xml:lang="en" type="main">Main title</title>
<!-- only one title element with type main -->
<title xml:lang="de" type="official">German title</title>
<!-- can have more titles of type official -->
</titles>
<description>description of the record</description>
<categories>
<category id="122">
<name>category name</name>
<description>category description</description>
</category>
<!-- can have more categories -->
</categories>
<tags>
<tag id="5434">
<name>tag name</name>
<description>tag description</description>
</tag>
<!-- can have more tags -->
</tags>
</record>
How do I select the data from these xml files using LINQ, or should I use something else?
You can load xml into XDocument objects using either the Load() method
for files, or the Parse() method for strings:
var doc = XDocument.Load("your-file.xml");
// OR
var doc = XDocument.Parse(yourXmlString);
Then you can access the data using LINQ:
var titles =
from title in doc.XPathSelectElements("//title")
where title.Attribute("type").Value == "official"
select title.Value;
Was searching for examples of Xmlserializer and found this: How to Deserialize XML document
So why not to try. I did Ctrl+C and Edit -> Paste Special -> Paste XML As Classes in Visual Studio 2013 and... Whoa I got all the classes generated. One condition target framework must be 4.5 and this function is available from Visual Studio 2012+ (as stated in that post)

I cannot access XML data using LINQ to XML from a file

I have the following XML in a file called contents.xml that is in the data directory in my C drive:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://datacenter1.table.core.windows.net/"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
<title type="text">TestContents</title>
<entry>
<title type="text" />
<author>
<name />
</author>
<link rel="edit" title="TestContents" />
<content type="application/xml">
<m:properties>
<d:PartitionKey>0100000</d:PartitionKey>
<d:Text>xx</d:Text>
</m:properties>
</content>
</entry>
<entry>
<title type="text" />
<updated />
I need to get the values of the <d:Text> so I created this console application:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XDocument xmlDoc = XDocument.Load(#"c:\data\contents.xml");
var q = from c in xmlDoc.Descendants("entry")
select (string)c.Element("link") ;
Console.WriteLine(q);
}
}
}
But I have two problems. Firstly the console shows but then disappears before I can see the output. Second if I look at q in the debugger it says "enumeration returns no results".
What's the best way that I can use to get what I really need which is the values of the many <d:Text> ?
First Problem: After Console.WriteLine(q) line, you can write Console.ReadLine(). So result will be displayed until you press "Enter".
Second Problem: If you want all values in a list then u can do q.ToList()

How do I make a contract object model equivalent to what I need the response XML to look like in WCF?

I feel I have done this many times, but it seems like I have to jump through too many hoops, and I am wondering if there is an easier way.
I am using WCF to build out an API (REST and SOAP endpoints). I am building out what I would like the XML response to look like from one of my calls, and I would like to know the easiest way to get its equivalent object model (Data Contracts).
here is a sample XML request where GetSectionInvitesResponse is the top level contract that should be returned from the API call.
<GetSectionInvitesResponse>
<UserID></UserID>
<OrganizationInvites>
<SectionInvites>
<SectionSubscriber>
<Section>
<ID></ID>
<Name></Name>
<Description></Description>
<Descriptor></Descriptor>
<ParentID></ParentID>
</Section>
<SectionSubscriberID>
</SectionSubscriber>
<SectionSubscriber>
<Section>
<ID></ID>
<Name></Name>
<Description></Description>
<Descriptor></Descriptor>
<ParentID></ParentID>
</Section>
<SectionSubscriberID>
</SectionSubscriber>
</SectionInvites>
</OrganizationInvites>
<OrganizationInvites>
<SectionInvites>
<SectionSubscriber>
<Section>
<ID></ID>
<Name></Name>
<Description></Description>
<Descriptor></Descriptor>
<ParentID></ParentID>
</Section>
<SectionSubscriberID>
</SectionSubscriber>
<SectionSubscriber>
<Section>
<ID></ID>
<Name></Name>
<Description></Description>
<Descriptor></Descriptor>
<ParentID></ParentID>
</Section>
<SectionSubscriberID>
</SectionSubscriber>
<SectionSubscriber>
<Section>
<ID></ID>
<Name></Name>
<Description></Description>
<Descriptor></Descriptor>
<ParentID></ParentID>
</Section>
<SectionSubscriberID>
</SectionSubscriber>
</SectionInvites>
</OrganizationInvites>
</GetSectionInvitesResponse>
EDIT
Because I was not clear enough in my initial post, I want to make more clear what I am aiming to gain from this question.
I want to know the best way to expose this over SOAP and REST with minimal duplicated code while following the same XML schema as shown above?
In theory you can:
Paste the sample xml into your favorte xml editor
Have the editor auto generate an xml schema for you. In Visual Studio its XML->Generate Schema. For example InvitesResponse.xsd.
From a command prompt run svcutil /dconly InvitesResponse.xsd /language:C# to create a DataContract file.
Out of curiosity I went through these steps and discovered:
<SectionSubscriberID> isn't closed correctly in your xml.
That the DataContractSerializer doesn't allow node sequences the way you currently have them defined.
svcutil output:
Error: Type 'GetSectionInvitesResponse' in namespace '' cannot be
imported. 'maxOccurs' on element 'OrganizationInvites' must be 1.
Either c hange the schema so that the types can map to data contract
types or use ImportXmlType or use a different serializer.
So I found the DataContract Schema Reference that points out that in a complex type must have maxOccurs = 1.
You will probably have to change your schema if you want to keep the DataContract serializer and not switch to the XmlSerializer... which you would also discover if you just started coding.
It is at this point that the wisdom of #John Sanders kicked in and I stopped.

Reading different XML with C#/DOM

I am currently using DOM to navigate xml in my C# project. However, some XML that i've come across lately is a bit different.
whereas usually I have:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
<author>
<name>Me =)</name>
</author>
<content>
<somefield1>
<Subfield>subfield data</subfield>
</somefield>
</content>
</entry>
</feed>
and can navigate using foreach entry as entry, selectsinglenode(/content/somefield1/subfield), innertext to get the data from the subfield for each entry, the new XML looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:entry>
<atom:author>
<name>Me =)</name>
</atom:author>
<atom:content>
<somefield1>
<Subfield>subfield data</subfield>
</somefield>
</atom:content>
</atom:entry>
</atom:feed>
selectsinglenode(/atom:content/somefield1/subfield) is definitely not going to work...any suggestions?
atom: is just the namespace, and possibly you might just ignore it. If it still not works, you may have to use:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("atom", "http://www.w3.org/2005/Atom");
selectsinglenode("atom:content/somefield1/subfield", nsmgr);
Which is documented here

Categories

Resources