Created
December 5, 2013 21:19
-
-
Save thinkAmi/7814095 to your computer and use it in GitHub Desktop.
独自の名前空間を持つXMLを、LINQやXPathを使い、いろいろな方法でデータ取得する例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
// Add | |
using System.Net; | |
using AmazonProductAdvtApi; | |
using System.Xml; | |
using System.Xml.XPath; | |
using System.Xml.Linq; | |
namespace Hoge | |
{ | |
class Program | |
{ | |
const string AwsAccessKeyId = "YOUR KEY"; | |
const string AwsSecretKey = "YOUR SECRET KEY"; | |
const string AssociateTag = "YOUR TAG"; | |
const string Asin = "4873731410"; | |
/// <summary> | |
/// エントリポイント | |
/// </summary> | |
/// <param name="args"></param> | |
static void Main(string[] args) | |
{ | |
var requestUrl = CreateRequestUrl(Asin); | |
// LINQ to XML + XPathSelectElements | |
using (var response = HttpWebRequest.Create(requestUrl).GetResponse()) | |
{ | |
using (var stream = response.GetResponseStream()) | |
{ | |
Write(UseLinqXPathSelectElements(stream), "LINQ to XML + Descendants ver"); | |
} | |
} | |
// LINQ to XML + Descendants | |
using (var response = HttpWebRequest.Create(requestUrl).GetResponse()) | |
{ | |
using (var stream = response.GetResponseStream()) | |
{ | |
Write(UseLinqDescendants(stream), "LINQ to XML + Descendants ver"); | |
} | |
} | |
// XmlDocument + SelectNodes | |
using (var response = HttpWebRequest.Create(requestUrl).GetResponse()) | |
{ | |
using (var stream = response.GetResponseStream()) | |
{ | |
Write(UseXmlDocumentSelectNodes(stream), "XmlDocument + SelectNodes ver"); | |
} | |
} | |
// XPathNavigator + Evaluate | |
using (var response = HttpWebRequest.Create(requestUrl).GetResponse()) | |
{ | |
using (var stream = response.GetResponseStream()) | |
{ | |
Write(UseXnavigatorEvaluate(stream), "XPathNavigator + Evaluate ver"); | |
} | |
} | |
Console.WriteLine("Enterキーで終了"); | |
Console.ReadKey(); | |
} | |
/// <summary> | |
/// LINQ to XMLでXPathSelectElementsを使って取得 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <returns></returns> | |
static Program.Result UseLinqXPathSelectElements(System.IO.Stream stream) | |
{ | |
// XElementへストリームをロード | |
var root = XElement.Load(stream); | |
// 名前空間の作成 | |
string nameSpace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01"; | |
var nsmgr = new XmlNamespaceManager(new NameTable()); | |
nsmgr.AddNamespace("ns", nameSpace); | |
// 結果をProgram.Resultクラスへ詰め込み | |
// 注) オペレーションがItemLookupのためItemは1個しかないはずだが、Selectを使いたいためにXPathSelectElement"s" を使っている | |
var result = root.XPathSelectElements("//ns:Item", nsmgr) | |
.Select(item => new Program.Result | |
{ | |
Title = item.XPathSelectElement("./ns:ItemAttributes/ns:Title", nsmgr).Value, | |
TotalNew = int.Parse(item.XPathSelectElement("./ns:OfferSummary/ns:TotalNew", nsmgr).Value), | |
LowestNewPrice = int.Parse(item.XPathSelectElement("./ns:OfferSummary/ns:LowestNewPrice/ns:Amount", nsmgr).Value), | |
TotalUsed = int.Parse(item.XPathSelectElement("./ns:OfferSummary/ns:TotalUsed", nsmgr).Value), | |
LowestUsedPrice = int.Parse(item.XPathSelectElement("./ns:OfferSummary/ns:LowestUsedPrice/ns:Amount", nsmgr).Value), | |
TotalOffers = int.Parse(item.XPathSelectElement("./ns:Offers/ns:TotalOffers", nsmgr).Value), | |
}).First(); | |
return result; | |
} | |
/// <summary> | |
/// LINQ to XMLでDescendantsを使って取得 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <returns></returns> | |
static Program.Result UseLinqDescendants(System.IO.Stream stream) | |
{ | |
// XElementにストリームをロード | |
var root = XElement.Load(stream); | |
// 名前空間の作成 | |
XNamespace nameSpace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01"; | |
// 結果をProgram.Resultクラスへ詰め込み | |
var result = root.Descendants(nameSpace + "Item") | |
.Select(item => new Program.Result | |
{ | |
Title = item.Element(nameSpace + "ItemAttributes").Element(nameSpace + "Title").Value, | |
TotalNew = (int)item.Element(nameSpace + "OfferSummary").Element(nameSpace + "TotalNew"), | |
LowestNewPrice = (int)item.Element(nameSpace + "OfferSummary").Element(nameSpace + "LowestNewPrice").Element(nameSpace + "Amount"), | |
TotalUsed = (int)item.Element(nameSpace + "OfferSummary").Element(nameSpace + "TotalUsed"), | |
LowestUsedPrice = (int)item.Element(nameSpace + "OfferSummary").Element(nameSpace + "LowestUsedPrice").Element(nameSpace + "Amount"), | |
TotalOffers = (int)item.Element(nameSpace + "Offers").Element(nameSpace + "TotalOffers") | |
}).First(); | |
return result; | |
} | |
/// <summary> | |
/// XmlDocument + SelectNodesを使って取得 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <returns></returns> | |
static Program.Result UseXmlDocumentSelectNodes(System.IO.Stream stream) | |
{ | |
// XmlDocumentへストリームをロード | |
var root = new XmlDocument(); | |
root.Load(stream); | |
// 名前空間の作成 | |
string nameSpace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01"; | |
var nsmgr = new XmlNamespaceManager(root.NameTable); | |
nsmgr.AddNamespace("ns", nameSpace); | |
// 結果をProgram.Resultクラスへ詰め込み | |
var result = new Program.Result | |
{ | |
Title = root.SelectNodes("//ns:Title", nsmgr).Item(0).InnerText, | |
TotalNew = int.Parse(root.SelectNodes("//ns:TotalNew", nsmgr).Item(0).InnerText), | |
LowestNewPrice = int.Parse(root.SelectNodes("//ns:LowestNewPrice/ns:Amount", nsmgr).Item(0).InnerText), | |
TotalUsed = int.Parse(root.SelectNodes("//ns:TotalUsed", nsmgr).Item(0).InnerText), | |
LowestUsedPrice = int.Parse(root.SelectNodes("//ns:LowestUsedPrice/ns:Amount", nsmgr).Item(0).InnerText), | |
TotalOffers = int.Parse(root.SelectNodes("//ns:TotalOffers", nsmgr).Item(0).InnerText) | |
}; | |
return result; | |
} | |
/// <summary> | |
/// XPathNavigator + Evaluateを使って取得 | |
/// </summary> | |
/// <param name="stream"></param> | |
/// <returns></returns> | |
static Program.Result UseXnavigatorEvaluate(System.IO.Stream stream) | |
{ | |
// XPathNavigatorへストリームをロード | |
var root = new XPathDocument(stream).CreateNavigator(); | |
// 名前空間の作成 | |
string nameSpace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01"; | |
var nsmgr = new XmlNamespaceManager(root.NameTable); | |
nsmgr.AddNamespace("ns", nameSpace); | |
// 結果をProgram.Resultクラスへ詰め込み | |
var result = new Program.Result(); | |
foreach (XPathNavigator r in root.Evaluate("//ns:Title", nsmgr) as XPathNodeIterator) { result.Title = r.Value; } | |
foreach (XPathNavigator r in root.Evaluate("//ns:TotalNew", nsmgr) as XPathNodeIterator) { result.TotalNew = int.Parse(r.Value); } | |
foreach (XPathNavigator r in root.Evaluate("//ns:LowestNewPrice/ns:Amount", nsmgr) as XPathNodeIterator) { result.LowestNewPrice = int.Parse(r.Value); } | |
foreach (XPathNavigator r in root.Evaluate("//ns:TotalUsed", nsmgr) as XPathNodeIterator) { result.TotalUsed = int.Parse(r.Value); } | |
foreach (XPathNavigator r in root.Evaluate("//ns:LowestUsedPrice/ns:Amount", nsmgr) as XPathNodeIterator) { result.LowestUsedPrice = int.Parse(r.Value); } | |
foreach (XPathNavigator r in root.Evaluate("//ns:TotalOffers", nsmgr) as XPathNodeIterator) { result.TotalOffers = int.Parse(r.Value); } | |
return result; | |
} | |
/// <summary> | |
/// AmazonへリクエストするUrlを作成する | |
/// </summary> | |
/// <param name="asin">対象のASIN</param> | |
/// <returns></returns> | |
static string CreateRequestUrl(string asin) | |
{ | |
var parameter = new Dictionary<string, string>() | |
{ | |
{ "Service", "AWSECommerceService" }, | |
{ "Version", "2011-08-01" }, | |
{ "Operation", "ItemLookup" }, | |
{ "ResponseGroup", "Offers,Small" }, | |
{ "ItemId", asin}, | |
{ "Condition", "All"} | |
}; | |
var helper = new SignedRequestHelper(AwsAccessKeyId, AwsSecretKey, "ecs.amazonaws.jp", AssociateTag); | |
var requestUrl = helper.Sign(parameter); | |
return requestUrl; | |
} | |
/// <summary> | |
/// コンソールへの表示 | |
/// </summary> | |
/// <param name="result">パース結果</param> | |
/// <param name="mode">処理したモード</param> | |
static void Write(Result result, string mode) | |
{ | |
Console.WriteLine("---------------------------"); | |
Console.WriteLine(mode); | |
Console.WriteLine("---------------------------"); | |
Console.WriteLine("Title: " + result.Title); | |
Console.WriteLine("TotalNew: " + result.TotalNew.ToString()); | |
Console.WriteLine("LowestNewPrice: " + result.LowestNewPrice.ToString()); | |
Console.WriteLine("TotalUsed: " + result.TotalUsed.ToString()); | |
Console.WriteLine("LowestUsedPrice: " + result.LowestUsedPrice.ToString()); | |
Console.WriteLine("TotalOffers: " + result.TotalOffers); | |
Console.WriteLine("---------------------------"); | |
} | |
/// <summary> | |
/// パース結果を入れるクラス | |
/// </summary> | |
public class Result | |
{ | |
public string Title { get; set; } | |
public int TotalNew { get; set; } | |
public int LowestNewPrice { get; set; } | |
public int TotalUsed { get; set; } | |
public int LowestUsedPrice { get; set; } | |
public int TotalOffers { get; set; } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/********************************************************************************************** | |
* Copyright 2009 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file | |
* except in compliance with the License. A copy of the License is located at | |
* | |
* http://aws.amazon.com/apache2.0/ | |
* | |
* or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" | |
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
* License for the specific language governing permissions and limitations under the License. | |
* | |
* ******************************************************************************************** | |
* | |
* Amazon Product Advertising API | |
* Signed Requests Sample Code | |
* | |
* API Version: 2009-03-31 | |
* | |
* | |
* / | |
* | |
* --------------------------------------------------------- | |
* thinkAmi Change Log | |
* --------------------------------------------------------- | |
* # 2013/11/24 ver | |
* Change list for API Version: 2011-08-01 | |
* - Add public Constractor(4 arguments): | |
* - Change Constractor(3 arguments): public to private | |
* - Add AssociateTag | |
* | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Web; | |
using System.Security.Cryptography; | |
namespace AmazonProductAdvtApi | |
{ | |
public class SignedRequestHelper | |
{ | |
private string endPoint; | |
private string akid; | |
private byte[] secret; | |
private HMAC signer; | |
// AssociateTag | |
private string atag; | |
private const string REQUEST_URI = "/onca/xml"; | |
private const string REQUEST_METHOD = "GET"; | |
/* | |
* Use this constructor to create the object. The AWS credentials are available on | |
* http://aws.amazon.com | |
* | |
* The destination is the service end-point for your application: | |
* US: ecs.amazonaws.com | |
* JP: ecs.amazonaws.jp | |
* UK: ecs.amazonaws.co.uk | |
* DE: ecs.amazonaws.de | |
* FR: ecs.amazonaws.fr | |
* CA: ecs.amazonaws.ca | |
*/ | |
private SignedRequestHelper(string awsAccessKeyId, string awsSecretKey, string destination) | |
{ | |
this.endPoint = destination.ToLower(); | |
this.akid = awsAccessKeyId; | |
this.secret = Encoding.UTF8.GetBytes(awsSecretKey); | |
this.signer = new HMACSHA256(this.secret); | |
} | |
/// <summary> | |
/// Constractor for API Version: 2011-08-01 | |
/// </summary> | |
/// <param name="awsAccessKeyId"></param> | |
/// <param name="awsSecretKey"></param> | |
/// <param name="destination"></param> | |
/// <param name="associateTag"></param> | |
public SignedRequestHelper(string awsAccessKeyId, string awsSecretKey, string destination, string associateTag) | |
: this(awsAccessKeyId, awsSecretKey, destination) | |
{ | |
this.atag = associateTag; | |
} | |
/* | |
* Sign a request in the form of a Dictionary of name-value pairs. | |
* | |
* This method returns a complete URL to use. Modifying the returned URL | |
* in any way invalidates the signature and Amazon will reject the requests. | |
*/ | |
public string Sign(IDictionary<string, string> request) | |
{ | |
// Use a SortedDictionary to get the parameters in naturual byte order, as | |
// required by AWS. | |
ParamComparer pc = new ParamComparer(); | |
SortedDictionary<string, string> sortedMap = new SortedDictionary<string, string>(request, pc); | |
// Add the AWSAccessKeyId and Timestamp to the requests. | |
sortedMap["AWSAccessKeyId"] = this.akid; | |
sortedMap["Timestamp"] = this.GetTimestamp(); | |
sortedMap["AssociateTag"] = this.atag; // Add | |
// Get the canonical query string | |
string canonicalQS = this.ConstructCanonicalQueryString(sortedMap); | |
// Derive the bytes needs to be signed. | |
StringBuilder builder = new StringBuilder(); | |
builder.Append(REQUEST_METHOD) | |
.Append("\n") | |
.Append(this.endPoint) | |
.Append("\n") | |
.Append(REQUEST_URI) | |
.Append("\n") | |
.Append(canonicalQS); | |
string stringToSign = builder.ToString(); | |
byte[] toSign = Encoding.UTF8.GetBytes(stringToSign); | |
// Compute the signature and convert to Base64. | |
byte[] sigBytes = signer.ComputeHash(toSign); | |
string signature = Convert.ToBase64String(sigBytes); | |
// now construct the complete URL and return to caller. | |
StringBuilder qsBuilder = new StringBuilder(); | |
qsBuilder.Append("http://") | |
.Append(this.endPoint) | |
.Append(REQUEST_URI) | |
.Append("?") | |
.Append(canonicalQS) | |
.Append("&Signature=") | |
.Append(this.PercentEncodeRfc3986(signature)); | |
return qsBuilder.ToString(); | |
} | |
/* | |
* Sign a request in the form of a query string. | |
* | |
* This method returns a complete URL to use. Modifying the returned URL | |
* in any way invalidates the signature and Amazon will reject the requests. | |
*/ | |
public string Sign(string queryString) | |
{ | |
IDictionary<string, string> request = this.CreateDictionary(queryString); | |
return this.Sign(request); | |
} | |
/* | |
* Current time in IS0 8601 format as required by Amazon | |
*/ | |
private string GetTimestamp() | |
{ | |
DateTime currentTime = DateTime.UtcNow; | |
string timestamp = currentTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); | |
return timestamp; | |
} | |
/* | |
* Percent-encode (URL Encode) according to RFC 3986 as required by Amazon. | |
* | |
* This is necessary because .NET's HttpUtility.UrlEncode does not encode | |
* according to the above standard. Also, .NET returns lower-case encoding | |
* by default and Amazon requires upper-case encoding. | |
*/ | |
private string PercentEncodeRfc3986(string str) | |
{ | |
str = HttpUtility.UrlEncode(str, System.Text.Encoding.UTF8); | |
str = str.Replace("'", "%27").Replace("(", "%28").Replace(")", "%29").Replace("*", "%2A").Replace("!", "%21").Replace("%7e", "~").Replace("+", "%20"); | |
StringBuilder sbuilder = new StringBuilder(str); | |
for (int i = 0; i < sbuilder.Length; i++) | |
{ | |
if (sbuilder[i] == '%') | |
{ | |
if (Char.IsLetter(sbuilder[i + 1]) || Char.IsLetter(sbuilder[i + 2])) | |
{ | |
sbuilder[i + 1] = Char.ToUpper(sbuilder[i + 1]); | |
sbuilder[i + 2] = Char.ToUpper(sbuilder[i + 2]); | |
} | |
} | |
} | |
return sbuilder.ToString(); | |
} | |
/* | |
* Convert a query string to corresponding dictionary of name-value pairs. | |
*/ | |
private IDictionary<string, string> CreateDictionary(string queryString) | |
{ | |
Dictionary<string, string> map = new Dictionary<string, string>(); | |
string[] requestParams = queryString.Split('&'); | |
for (int i = 0; i < requestParams.Length; i++) | |
{ | |
if (requestParams[i].Length < 1) | |
{ | |
continue; | |
} | |
char[] sep = { '=' }; | |
string[] param = requestParams[i].Split(sep, 2); | |
for (int j = 0; j < param.Length; j++) | |
{ | |
param[j] = HttpUtility.UrlDecode(param[j], System.Text.Encoding.UTF8); | |
} | |
switch (param.Length) | |
{ | |
case 1: | |
{ | |
if (requestParams[i].Length >= 1) | |
{ | |
if (requestParams[i].ToCharArray()[0] == '=') | |
{ | |
map[""] = param[0]; | |
} | |
else | |
{ | |
map[param[0]] = ""; | |
} | |
} | |
break; | |
} | |
case 2: | |
{ | |
if (!string.IsNullOrEmpty(param[0])) | |
{ | |
map[param[0]] = param[1]; | |
} | |
} | |
break; | |
} | |
} | |
return map; | |
} | |
/* | |
* Consttuct the canonical query string from the sorted parameter map. | |
*/ | |
private string ConstructCanonicalQueryString(SortedDictionary<string, string> sortedParamMap) | |
{ | |
StringBuilder builder = new StringBuilder(); | |
if (sortedParamMap.Count == 0) | |
{ | |
builder.Append(""); | |
return builder.ToString(); | |
} | |
foreach (KeyValuePair<string, string> kvp in sortedParamMap) | |
{ | |
builder.Append(this.PercentEncodeRfc3986(kvp.Key)); | |
builder.Append("="); | |
builder.Append(this.PercentEncodeRfc3986(kvp.Value)); | |
builder.Append("&"); | |
} | |
string canonicalString = builder.ToString(); | |
canonicalString = canonicalString.Substring(0, canonicalString.Length - 1); | |
return canonicalString; | |
} | |
} | |
/* | |
* To help the SortedDictionary order the name-value pairs in the correct way. | |
*/ | |
class ParamComparer : IComparer<string> | |
{ | |
public int Compare(string p1, string p2) | |
{ | |
return string.CompareOrdinal(p1, p2); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment