Skip to content

Instantly share code, notes, and snippets.

@thinkAmi
Created December 5, 2013 21:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkAmi/7814095 to your computer and use it in GitHub Desktop.
Save thinkAmi/7814095 to your computer and use it in GitHub Desktop.
独自の名前空間を持つXMLを、LINQやXPathを使い、いろいろな方法でデータ取得する例
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; }
}
}
}
/**********************************************************************************************
* 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