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:
- Is WCF the best technology to do this in .NET 4.5 or above?
- Which are the appropriate classes/methods to connect, send the GET or POST request and await the response?
- 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());
}
}