使用 REST XML Web 服务
Posted
技术标签:
【中文标题】使用 REST XML Web 服务【英文标题】:Consuming a REST XML web service 【发布时间】:2011-06-09 00:37:33 【问题描述】:我正在尝试使用以下 Web 服务 http://ipinfodb.com/ip_location_api.php 此 Web 服务返回一个 xml 响应,下面的代码获取 XML 响应,但不知何故,当从 XML 响应中调整值时它不起作用。
我的代码有什么问题?
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Net;
using System.Xml;
namespace ConsoleApplication3
class Program
static void Main(string[] args)
HttpWebRequest request = null;
HttpWebResponse response = null;
String Xml;
// Create the web request
request = WebRequest.Create("http://api.ipinfodb.com/v2/ip_query.php?key=<yourkey>&ip=74.125.45.100&timezone=true") as HttpWebRequest;
// Get response
using (response = request.GetResponse() as HttpWebResponse)
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
Xml = reader.ReadToEnd();
// Console xml output
Console.WriteLine(Xml); //see if we get the xml response, (YES we do)
Console.ReadLine();
string _currentField = "";
StringReader _sr = new StringReader(Xml);
XmlTextReader _xtr = new XmlTextReader(_sr);
_xtr.XmlResolver = null;
_xtr.WhitespaceHandling = WhitespaceHandling.None;
// get the root node
_xtr.Read();
if ((_xtr.NodeType == XmlNodeType.Element) && (_xtr.Name == "Response"))
while (_xtr.Read())
if ((_xtr.NodeType == XmlNodeType.Element) && (!_xtr.IsEmptyElement))
_currentField = _xtr.Name;
_xtr.Read();
if (_xtr.NodeType == XmlNodeType.Text)
switch (_currentField)
case "Status":
Console.WriteLine(_xtr.Value); //we print to console for testing purposes, normally assign it to a variable here!
break;
case "CountryCode":
Console.WriteLine(_xtr.Value);
break;
case "CountryName":
Console.WriteLine(_xtr.Value);
break;
case "RegionCode":
Console.WriteLine(_xtr.Value);
break;
case "RegionName":
Console.WriteLine(_xtr.Value);
break;
case "City":
Console.WriteLine(_xtr.Value);
break;
case "ZipPostalCode":
Console.WriteLine(_xtr.Value);
break;
case "Latitude":
Console.WriteLine(_xtr.Value);
break;
case "Longitude":
Console.WriteLine(_xtr.Value);
break;
case "Gmtoffset":
Console.WriteLine(_xtr.Value);
break;
case "Dstoffset":
Console.WriteLine(_xtr.Value);
break;
case "TimezoneName":
Console.WriteLine(_xtr.Value);
break;
case "Isdst":
Console.WriteLine(_xtr.Value);
break;
case "Ip":
Console.WriteLine(_xtr.Value);
break;
default:
// unknown field
throw new Exception("Unknown field in response.");
Console.ReadLine();
编辑:这是返回的 XML 响应
<?xml version="1.0" encoding="UTF-8" ?>
- <Response>
<Status>OK</Status>
<CountryCode>US</CountryCode>
<CountryName>United States</CountryName>
<RegionCode>06</RegionCode>
<RegionName>California</RegionName>
<City>Mountain View</City>
<ZipPostalCode>94043</ZipPostalCode>
<Latitude>37.4192</Latitude>
<Longitude>-122.057</Longitude>
<Gmtoffset>-28800</Gmtoffset>
<Dstoffset>0</Dstoffset>
<TimezoneName>America/Los_Angeles</TimezoneName>
<Isdst>0</Isdst>
<Ip>74.125.45.100</Ip>
</Response>
【问题讨论】:
【参考方案1】:我的解决方案是:
对结果 XML 运行两次 xsd.exe
实用程序,将其转换为 XSD(第一步)和 C# 类(第二步) - 这将为您提供 C# 类 Response
接下来,您可以轻松地将响应反序列化为该类的实例:
HttpWebRequest request = WebRequest.Create("http://api.ipinfodb.com/v2/ip_query.php?key=--yourkey--&ip=74.125.45.100&timezone=true") as HttpWebRequest;
XmlSerializer ser = new XmlSerializer(typeof(Response));
WebResponse response = request.GetResponse();
var result = ser.Deserialize(response.GetResponseStream());
现在您的result
将包含Response
的一个实例,所有元素都是对象中的好字段。
在其MSDN doc page 上阅读有关 xsd.exe 的更多信息。
【讨论】:
【参考方案2】:我使用相同的 API,我将响应 XML 加载到 XDocument 并解析例如
// build URL up at runtime
string apiKey = ConfigurationManager.AppSettings["geoApiKey"];
string url = String.Format(ConfigurationManager.AppSettings["geoApiUrl"], apiKey, ip);
WebRequest request = WebRequest.Create(url);
try
WebResponse response = request.GetResponse();
using (var sr = new System.IO.StreamReader(response.GetResponseStream()))
XDocument xmlDoc = new XDocument();
try
xmlDoc = XDocument.Parse(sr.ReadToEnd());
string status = xmlDoc.Root.Element("Status").Value;
Console.WriteLine("Response status: 0", status);
if (status == "OK")
// if the status is OK it's normally safe to assume the required elements
// are there. However, if you want to be safe you can always check the element
// exists before retrieving the value
Console.WriteLine(xmlDoc.Root.Element("CountryCode").Value);
Console.WriteLine(xmlDoc.Root.Element("CountryName").Value);
...
catch (Exception)
// handle if necessary
catch (WebException)
// handle if necessary
您还应该做的是引入一个自定义类,例如GeoLocationInfo
并将您的代码包装在一个函数中,例如GetGeoLocation(string ip)
然后,您可以填充并返回该类的实例,而不是将信息写入控制台窗口。
【讨论】:
【参考方案3】:您假设第一个节点将是根节点,但这是不正确的。您将首先拥有XmlDeclaration
节点,然后可能是Whitespace
节点。所以你可能应该像
...
bool isRootRead = false;
while (_xtr.Read())
if (_xtr.NodeType == XmlNodeType.Element)
if (!isRootRead)
if (_xter.Name == "Response")
// root found
isRootRead = true;
// jump to next node if root node / ignore other nodes till root element is read
continue;
_currentField = _xtr.Name;
_xtr.Read();
if (_xtr.NodeType == XmlNodeType.Text)
switch (_currentField)
case "Status":
Console.WriteLine(_xtr.Value); //we print to console for testing purposes, normally assign it to a variable here!
break;
...
但说了这么多,我个人更喜欢创建响应 XSD(如果 Web 服务提供它会更好)并从中生成类(使用 XSD.exe 或 Xsd2Code)以对其进行序列化/反序列化。
【讨论】:
【参考方案4】:我认为您需要使用 _xtr.MoveToContent();使用read方法之前的方法.. 看看有没有用
【讨论】:
以上是关于使用 REST XML Web 服务的主要内容,如果未能解决你的问题,请参考以下文章
使用 KissXML 处理 wcf Rest Web 服务 - 我应该如何处理命名空间问题
REST及REST风格的Web服务与ArcGIS Server REST风格的Web服务 一
如何使用Jersey在REST Web服务中为服务类指定多个包名称
在 Play 中请求 Content-Type! REST Web 服务框架