2

I want to consume a remote REST service using WCF in .NET 4.5 or above. It is for some desktop application software I am writing. I am electing to use WCF as I currently believe it to be the most appropriate technology in .NET unless anyone says otherwise.

The service is accessed by either GET or POST (the same response) and can return either JSON or XML which is specified as a URL parameter. However, even though it can return XML, there is no WSDL file.

I want to access this service without the use of any third party libraries.

My questions:

  1. Is WCF the best technology to do this in .NET 4.5 or above?
  2. Which are the appropriate classes/methods to connect, send the GET or POST request and await the response?
  3. Is there a way to tell WCF to automagically fill my model classes with the de-serialized json or xml and if not which is the latest/recommended de-serialization class within .NET?

Here is an example URL of the service:

XML: http://www.expasy.org/cgi-bin/prosite/PSScan.cgi?seq=ENTK_HUMAN&output=xml

JSON: http://www.expasy.org/cgi-bin/prosite/PSScan.cgi?seq=ENTK_HUMAN&output=json

Here is an example of what is returned by the service in XML:

<?xml version="1.0" encoding="UTF-8"?>
<matchset xmlns="urn:expasy:scanprosite" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:expasy:scanprosite http://expasy.org/tools/scanprosite/scanprosite.xsd" n_match="13" n_seq="1">
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>54</start>
    <stop>169</stop>
    <signature_ac>PS50024</signature_ac>
    <signature_id>SEA</signature_id>
    <score>32.979</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>183</start>
    <stop>222</stop>
    <signature_ac>PS50068</signature_ac>
    <signature_id>LDLRA_2</signature_id>
    <score>10.75</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>197</start>
    <stop>221</stop>
    <signature_ac>PS01209</signature_ac>
    <signature_id>LDLRA_1</signature_id>
    <level_tag>(0)</level_tag>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>225</start>
    <stop>334</stop>
    <signature_ac>PS01180</signature_ac>
    <signature_id>CUB</signature_id>
    <score>13.293</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>345</start>
    <stop>504</stop>
    <signature_ac>PS50060</signature_ac>
    <signature_id>MAM_2</signature_id>
    <score>42.203</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>391</start>
    <stop>431</stop>
    <signature_ac>PS00740</signature_ac>
    <signature_id>MAM_1</signature_id>
    <level_tag>(0)</level_tag>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>524</start>
    <stop>634</stop>
    <signature_ac>PS01180</signature_ac>
    <signature_id>CUB</signature_id>
    <score>17.206</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>642</start>
    <stop>678</stop>
    <signature_ac>PS50068</signature_ac>
    <signature_id>LDLRA_2</signature_id>
    <score>13.3</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>655</start>
    <stop>677</stop>
    <signature_ac>PS01209</signature_ac>
    <signature_id>LDLRA_1</signature_id>
    <level_tag>(0)</level_tag>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>678</start>
    <stop>788</stop>
    <signature_ac>PS50287</signature_ac>
    <signature_id>SRCR_2</signature_id>
    <score>16.02</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>785</start>
    <stop>1019</stop>
    <signature_ac>PS50240</signature_ac>
    <signature_id>TRYPSIN_DOM</signature_id>
    <score>39.104</score>
    <level>0</level>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>821</start>
    <stop>826</stop>
    <signature_ac>PS00134</signature_ac>
    <signature_id>TRYPSIN_HIS</signature_id>
    <level_tag>(0)</level_tag>
</match>
<match>
    <sequence_ac>P98073</sequence_ac>
    <sequence_id>ENTK_HUMAN</sequence_id>
    <sequence_db>sp</sequence_db>
    <start>965</start>
    <stop>976</stop>
    <signature_ac>PS00135</signature_ac>
    <signature_id>TRYPSIN_SER</signature_id>
    <level_tag>(0)</level_tag>
</match>
</matchset>

Here is an example of what is returned by the service in JSON:

{
"n_match" : 13, "n_seq" : 1,
"matchset" : [
{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 54, "stop" : 169, "signature_ac" : "PS50024", "signature_id" : "SEA", "score" : 32.979, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 183, "stop" : 222, "signature_ac" : "PS50068", "signature_id" : "LDLRA_2", "score" : 10.75, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 197, "stop" : 221, "signature_ac" : "PS01209", "signature_id" : "LDLRA_1", "level_tag" : "(0)"},{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 225, "stop" : 334, "signature_ac" : "PS01180", "signature_id" : "CUB", "score" : 13.293, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 345, "stop" : 504, "signature_ac" : "PS50060", "signature_id" : "MAM_2", "score" : 42.203, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 391, "stop" : 431, "signature_ac" : "PS00740", "signature_id" : "MAM_1", "level_tag" : "(0)"},{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 524, "stop" : 634, "signature_ac" : "PS01180", "signature_id" : "CUB", "score" : 17.206, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 642, "stop" : 678, "signature_ac" : "PS50068", "signature_id" : "LDLRA_2", "score" : 13.3, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 655, "stop" : 677, "signature_ac" : "PS01209", "signature_id" : "LDLRA_1", "level_tag" : "(0)"},{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 678, "stop" : 788, "signature_ac" : "PS50287", "signature_id" : "SRCR_2", "score" : 16.02, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 785, "stop" : 1019, "signature_ac" : "PS50240", "signature_id" : "TRYPSIN_DOM", "score" : 39.104, "level" : 0 },{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 821, "stop" : 826, "signature_ac" : "PS00134", "signature_id" : "TRYPSIN_HIS", "level_tag" : "(0)"},{"sequence_ac" : "P98073", "sequence_id" : "ENTK_HUMAN", "sequence_db" : "sp", "start" : 965, "stop" : 976, "signature_ac" : "PS00135", "signature_id" : "TRYPSIN_SER", "level_tag" : "(0)"} 
] }

Here are the model classes I already made, although I'm not sure if I needed to make them or if WCF can automagic one for me:

    public class PrositeScanMatchSet
    {
        public int n_match { get; set; }
        public int n_seq { get; set; }
        public PrositeScanMatch[] matchset { get; set; }
    }

    public class PrositeScanMatch
    {
        public string sequence_ac { get; set; }
        public string sequence_id { get; set; }
        public string sequence_db { get; set; }
        public int start { get; set; }
        public int stop { get; set; }
        public string signature_ac { get; set; }
        public string signature_id { get; set; }
        public float score { get; set; }
        public int level { get; set; }
        public string level_tag { get; set; }
    }

Also, here is a class I made to make the service query string:

public class ScanPrositeParameters
{
    /// <summary>
    /// Sequence(s) to be scanned: UniProtKB accessions e.g. P98073 or identifiers e.g. ENTK_HUMAN or PDB identifiers e.g. 4DGJ or sequences in FASTA format or UniProtKB/Swiss-Prot format. 
    /// Do not repeat parameter; multiple sequences can be specified by separating them with new lines(%0A in url).
    /// </summary>
    public string seq        ;

    /// <summary>
    /// Motif(s) to scan against: PROSITE accession e.g. PS50240 or identifier e.g. TRYPSIN_DOM or your own pattern e.g. P-x(2)-G-E-S-G(2)-[AS]. 
    /// If not specified, all PROSITE motifs are used.
    /// Do not repeat parameter; multiple motifs can be specified by separating them with new lines(%0A in url).
    /// </summary>
    public string sig        ;

    /// <summary>
    /// Target protein database for scans of motifs against whole protein databases: 'sp' (UniProtKB/Swiss-Prot) or 'tr' (UniProtKB/TrEMBL) or 'pdb' (PDB). 
    /// Only work if 'seq' is not defined.Parameter can be repeated; 1 target db by 'db' parameter.
    /// </summary>
    public string db         ;

    /// <summary>
    /// If true (defined, non empty, non zero): includes UniProtKB/Swiss-Prot splice variants. 
    /// Only works on scans against UniProtKB/Swiss-Prot.
    /// </summary>
    public string varsplic   ;

    /// <summary>
    /// Any taxonomical term e.g. 'Homo sapiens', e.g. 'Fungi; Arthropoda' or corresponding NCBI TaxID e.g. 9606, e.g. '4751; 6656' 
    /// Separate multiple terms with a semicolon.
    /// Only works on scans against UniProtKB/Swiss-Prot and UniProtKB/TrEMBL.
    /// </summary>
    public string lineage    ;

    /// <summary>
    /// Description (DE) filter: e.g. protease. 
    /// Only works on scans against UniProtKB/Swiss-Prot and UniProtKB/TrEMBL.
    /// </summary>
    public string description;

    /// <summary>
    /// Number of X characters in a scanned sequence that can be matched by a conserved position in a pattern. 
    /// Only works if 'sig' is defined, i.e.on scans of specific sequences/protein database(s) against specific motif(s). 
    /// Only works on scans against patterns.
    /// </summary>
    public string max_x      ;

    /// <summary>
    /// Output format: 'xml' or 'json' (or 'txt')
    /// </summary>
    public string output     ;

    /// <summary>
    /// If true (defined, non empty, non zero): excludes motifs with a high probability of occurrence. 
    /// Default: on.
    /// Only works if 'seq' is defined and 'sig' is not defined, i.e.on scans of specific sequence(s) against all PROSITE motifs.
    /// </summary>
    public string skip       ;

    /// <summary>
    /// If true (defined, non empty, non zero): shows matches with low level scores. 
    /// Default: off.
    /// Only works with PROSITE profiles.
    /// </summary>
    public string lowscore   ;

    /// <summary>
    /// If true (defined, non empty, non zero): does not scan against profiles. 
    /// Only works if 'seq' is defined and 'sig' is not defined, i.e.on scans of specific sequence(s) against all PROSITE motifs.
    /// </summary>
    public string noprofile  ;

    /// <summary>
    /// Mimimal number of hits per matched sequences. 
    /// Only works if 'sig' and 'db' are defined, i.e.on scans of protein database(s) against specific motif(s).
    /// </summary>
    public string minhits    ;

    public string QueryString()
    {
        var result = new Dictionary<string, string>()
        {
            {"seq", seq},
            {"sig", sig},
            {"db", db},
            {"varsplic", varsplic},
            {"lineage", lineage},
            {"description", description},
            {"max_x", max_x},
            {"output", output},
            {"skip", skip},
            {"lowscore", lowscore},
            {"noprofile", noprofile},
            {"minhits", minhits}
        };

        return String.Join("&", result.Where(a => a.Key != null && a.Value != null).Select(kvp => WebUtility.UrlEncode(kvp.Key) + "=" + WebUtility.UrlEncode(kvp.Value)).ToList());
    }

}
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Aalawlx
  • 593
  • 5
  • 25
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Jan 11 '15 at 03:37

3 Answers3

1

Using WCF seems like overkill when the server supports a REST Get interface.

The XDocument class can read a XML uri.

var doc = XDocument.Load("http://www.expasy.org/cgi-bin/prosite/PSScan.cgi?seq=ENTK_HUMAN&output=xml");

You can then use LINQ to locate data. See Basic Queries http://msdn.microsoft.com/en-us/library/bb943906.aspx for more details.

Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
1

I would recommend looking at HttpClient to retrieve JSon from the Remote Service.

http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client

Mike Burdick
  • 838
  • 6
  • 5
1

You have 3 questions here so I'm going to answer them all and build upon other people's answers: -

Is WCF the best technology to do this in .NET 4.5 or above?

WCF is a very powerful communication framework but as one of the answers above states; you could very easily just communicate with the REST server using a much simpler HttpClient. You mention you want to either use JSON/XML, which implies that, potentially, in the future, this may be expanded upon to support another format, so I believe that it would be more appropriate not to use WCF at this time and follow the concept of KISS. You should easily be able to swap out your connectivity code if you use an interface. (http://en.wikipedia.org/wiki/KISS_principle)

I'm sure you have already done a lot of research on WCF but if not, take a looks at http://msdn.microsoft.com/en-us/library/ms731082%28v=vs.110%29.aspx. The MSDN documentation is surprisingly good for getting started.

Which are the appropriate classes/methods to connect, send the GET or POST request and await the response?

This has already been answered; your client will know already whether it is requesting xml/json so you will just pass the data to the appropriate reader that will parse JSON/XML/other formats.

Luckily .NET has numerous APIs available to help you parse these languages and this will help answer your final question.

Is there a way to tell WCF to automagically fill my model classes with the de-serialized json or xml and if not which is the latest/recommended de-serialization class within .NET?

Most definitely..

JSON

See http://msdn.microsoft.com/en-us/library/bb412179%28v=vs.110%29.aspx

How to: Serialize and Deserialize JSON Data

That will show you how to de-serialise the JSON data into your model classes; which also elaborates on doing it with WCF aswell, so this might be very useful to you. This includes the recommended class to use for deserialising JSON using DataContractJsonSerializer. It's recommended to use this because these classes don't rely upon the C# compiler, whereas one of the more commonly used previous methods before .NET 3.5/4 did.

XML

Quite a high upvoted question/answer here for doing it with XML which includes example working code: -

How to Deserialize XML document

How to Deserialize XML document

Make sure you have a look at the non accepted answer that uses XSD as well as the accepted answer. Both viable solutions; use what you feel is right for you! :)

Good luck!

Community
  • 1
  • 1
ed_me
  • 3,338
  • 2
  • 24
  • 34