Unity3D的XML(-RPC)和C#

我是在自问自答。
这个世界上恐怕只有我一个人这么做,我花了一个星期的时间才完成。如果也有人想要在Unity里用XML(-RPC),那我就为这个人节省了一周的时间。
我想要做的是和游戏服务器商讨一些东西,比如排行榜。服务器谈论的是XML-RPC,我很快就发现这在Unity里并不容易。

原问题:Unity3D XML(-RPC) and C#

阅读 6.9k
2 个回答

建立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可能包括types: int, string 或者 Hashtable的对象。它将返回精美格式化的(简单)XML-RPC字符串,准备发送到服务器。

发送

把XML发送到服务器,你需要发出带有设置为text/xml的mine类型post请求。标准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);
    }    
}
  • 用UTF8把XML编码成ByteArray
  • 创建一个新的Unity WWWForm
  • 创建HashTable, 存储当前的http标头,覆盖内容类型为text/xml。
  • 发送lot给server
  • 建立协程等待答复

不适用Unity发送

我发现使用C#(MonoDevelop)的标准版本)开发library比用Uinty开发一切简单多了。所以如果想用C#做同样的逻辑发送,如下:

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字符串找到,它将返回该节点的值,如果找不到就null。这个解析器是特定于:简单的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

现在用protobuff 的比较多了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
logo
Stack Overflow 翻译
子站问答
访问
宣传栏