我必须是世界上唯一一个试图这样做的人,但考虑到我用了大约一个星期的时间来解决这个问题 – 我想如果有另一个人想在Unity中使用XML(-RPC) – 我会拯救他们一周的麻烦.
我想做的是与我们的一个游戏服务器谈谈排行榜等事情.这台服务器“会谈”XML-RPC,我很快发现在Unity中这并不容易.
我没有在Unity中找到标准函数来执行此操作而不会增加大量开销.所以我建立了以下程序.
public string buildXMLRPCRequest(Hashtable FieldArray,string MethodName) { string ReturnString = ""; ReturnString += "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" + "\n" + "<simpleRPC version=\"0.9\">" + "\n" + "<methodCall>" + "\n" + "<methodName>" + MethodName + "</methodName>" + "\n" + "<vector type=\"struct\">"; ReturnString += buildNode(FieldArray); ReturnString += "\n</vector>" + "\n</methodCall>" + "\n</simpleRPC>"; return ReturnString; } public string buildNode(Hashtable FieldArray) { string ReturnList = ""; foreach (DictionaryEntry Item in FieldArray) { string TypeName = "int"; string NodeType = "scalar"; Type myType = Item.Value.GetType(); string fieldValue = ""; if (myType == typeof(string) ) { TypeName = "string"; fieldValue = Item.Value.ToString(); } if (myType == typeof(Hashtable) ) { fieldValue = buildNode(Item.Value as Hashtable); NodeType = "vector"; TypeName = "struct"; } if (myType == typeof(int) ) { fieldValue = Item.Value.ToString(); TypeName = "int"; } var ThisNode = "\n<" + NodeType + " type=\"" + TypeName + "\" id=\"" + Item.Key + "\">" + fieldValue + "</" + NodeType + ">"; ReturnList += ThisNode; } return ReturnList; }
buildXMLRPCRequest用于构建XML.您可以使用要编码的字段来处理HashTable,其中可能包含以下类型的对象:int,string或Hashtable.它将返回一个精美格式化的(简单)XML-RPC字符串,该字符串已准备好发送到我们的服务器.
发送
要将XML发送到我们的服务器,您需要发出一个POST请求,其mime类型设置为text / xml. Unity中不能使用任何标准C#方法,但使用buildXMLRPCRequest逻辑的输出可以很好地工作.它能做什么:
在Unity中发送
我用过这段代码:
private void UnityPostXML( int Staging,string WebServer,string MethodName,Hashtable FieldArray) { string WebServiceURL = "http://LIVESERVER/"; if (Staging == 1) { WebServiceURL = "http://TESTSERVER"; } // Encode the text to a UTF8 byte arrray string XMLRequest = buildXMLRPCRequest(FieldArray,MethodName); System.Text.Encoding enc = System.Text.Encoding.UTF8; byte[] myByteArray = enc.GetBytes(XMLRequest); // Get the Unity WWWForm object (a post version) var form = new WWWForm(); var url = WebServiceURL; // Add a custom header to the request. // Change the content type to xml and set the character set var headers = form.headers; headers["Content-Type"]="text/xml;charset=UTF-8"; // Post a request to an URL with our rawXMLData and custom headers var www = new WWW(WebServiceURL,myByteArray,headers); // Start a co-routine which will wait until our servers comes back StartCoroutine(WaitForRequest(www)); } IEnumerator WaitForRequest(WWW www) { yield return www; // check for errors if (www.error == null) { Debug.Log("WWW Ok!: " + www.text); } else { Debug.Log("WWW Error: "+ www.error); } }
>使用UTF8将XML编码为ByteArray
>创建一个新的Unity WWWForm
>创建一个HashTable,存储当前的http标头(如果有的话)并将内容类型覆盖为text / xml
>将该批次发送到服务器
>设置一个等待回复的协同程序
没有Unity发送
我发现在C#中开发一个库(我使用MonoDevelop的标准版本)比使用Unity更简单,所以如果wnat做同样的事情,那么C#中的equivelant发送逻辑就会低于它.
private string NormalXMLCall(int Staging,Hashtable Fields) { // Figure out who to call string WebServiceURL = "http://LIVSERVER"; if (Staging == 1) { WebServiceURL = "http://TESTSERVER"; } WebServiceURL += WebServer; // Build the request XmlRpcParser parser = new XmlRpcParser(); string XMLRequest = parser.buildXMLRPCRequest(Fields,MethodName); // Fire it off HttpWebRequest httpRequest =(HttpWebRequest)WebRequest.Create(WebServiceURL); httpRequest.Method = "POST"; //Defining the type of the posted data as XML httpRequest.ContentType = "text/xml"; // string data = xmlDoc.InnerXml; byte[] bytedata = Encoding.UTF8.GetBytes(XMLRequest); // Get the request stream. Stream requestStream = httpRequest.GetRequestStream(); // Write the data to the request stream. requestStream.Write(bytedata,bytedata.Length); requestStream.Close(); //Get Response HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse(); // Get the stream associated with the response. Stream receiveStream = httpResponse.GetResponseStream (); // Pipes the stream to a higher level stream reader with the required encoding format. StreamReader readStream = new StreamReader (receiveStream,Encoding.UTF8); string ReceivedData = readStream.ReadToEnd (); httpResponse.Close (); readStream.Close (); return ReceivedData; } }
从XML中提取数据
我写了一个简单的解析器.应为下面的findNode函数的构造函数提供原始XML数据和要查找的子节点对象.如果该节点可以在XML字符串的最高级别找到,则它将返回该节点的值(作为字符串),如果找不到,则返回null.此解析器特定于“Simple XML-RPC”,需要一些工作来解码编码字符,但这应该很容易添加.
public string findNode(string Xml,string SearchForTag) { int NestCounter = 0; bool FoundTag = false; int FoundTagLevel = 0; string ReturnValue = null; // Break it down by "<" string [] TagArray = Xml.Split('<'); for (int i=0;i<TagArray.Length;i++) { if (i>175 && i<180) { int Hello=1; } string ThisLine = "<" + TagArray[i]; if (ThisLine.Length <= 1) continue; if ((ThisLine.Length >= 2) && (ThisLine.Substring(0,2) == "<?")) continue; if ((ThisLine.Length >= 3) && (ThisLine.Substring(0,3) == "<--")) continue; // It can be a vector or a scalar - vectors are full of scalars so we'll ThisLine = ThisLine.Replace(" "," "); ThisLine = ThisLine.Replace("</","</"); string [] FieldArray = ThisLine.Split(' '); bool AddLineToResult = FoundTag; // Nest counter is the level we are operating on. We only check the first // Level. When a vector is found we increase the NestCount and we won't // search for the ID if (NestCounter <= 1) { // Initial array we are looking on level 1 for (int a=0;a<FieldArray.Length;a++) { string ThisTag = FieldArray[a]; string [] TagValue = ThisTag.Split("=\"".tocharArray(),5); // Every TagValue is xx=yy pair... we want "ID=\"xxx\" if (TagValue.Length >= 3) { string TagName = TagValue[2]; if (TagName == SearchForTag) { FoundTag = true; FoundTagLevel = NestCounter; // This could be a vector or Scalar so find the ">" in this string // and start adding from there int TerminatePos = ThisLine.IndexOf(">"); if ((TerminatePos >= 0) && (TerminatePos < ThisLine.Length)) { ReturnValue = ThisLine.Substring(TerminatePos+1); } break; } } } } if (FieldArray.Length > 0) { string ThisField = FieldArray[0].ToLower(); /* * If we are in the loop where we have found the tag,* we haven't changed level and this is the end of a scalar it must * mean that the tag was a scalar so we can safely leave now. */ if ((FoundTag) && (FoundTagLevel == NestCounter) && (ThisField == "</scalar>")) { break; // return ReturnValue; } // If we end or leave a vector we change the NestCounter if (ThisField.IndexOf("<vector") >= 0) { NestCounter++; } else if (ThisField.IndexOf("</vector>") >= 0) { NestCounter--; } } // If we have found our tag and the nest counte goes below the level // we where looking at - it's time to leave if (FoundTag) { if (NestCounter <= FoundTagLevel) { break; //return ReturnValue; } } if (AddLineToResult) { ReturnValue += ThisLine; } } // You may wanna do some url decoding here.... return ReturnValue; }