Сборка XML для отправки на наши серверы
Я не смог найти стандартную функцию в 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. Ни один из стандартных методов C # не может быть использован в Unity, но использование этого с выводом логики 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);
}
}
- кодирует XML в ByteArray, используя UTF8
- Создать новый Unity WWWForm
- Создайте HashTable, сохраните текущие заголовки http (если есть) и перезапишите тип содержимого в text / xml
- Отправить этот лот на сервер
- Настройка сопрограммы, которая ждет ответа
Отправка без единства
Я обнаружил, что разработка библиотеки на C # (я использую стандартную версию MonoDevelop) намного проще, чем использование Unity для всего, поэтому логика отправки equivelant в C # ниже, если wnat делает то же самое.
private string NormalXMLCall(int Staging,
string WebServer,
string MethodName,
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, 0, 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, или ноль, если он не может его найти. Этот синтаксический анализатор специфичен для «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;
}