From 53a001ef38465e6483052360b0e4ba5f6ee64be4 Mon Sep 17 00:00:00 2001 From: greg-dove Date: Wed, 16 Oct 2019 14:27:37 +1300 Subject: [PATCH] First cut at addressing issue #466 (support for legacy decoding only, not for encoding). --- .../royale/mx/rpc/http/AbstractOperation.as | 135 +++++----- .../main/royale/mx/rpc/xml/ComplexString.as | 57 +++++ .../royale/mx/rpc/xml/SimpleXMLDecoder.as | 240 ++++++++++++++++++ .../projects/XML/src/main/royale/XMLList.as | 7 + 4 files changed, 373 insertions(+), 66 deletions(-) create mode 100644 frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/ComplexString.as create mode 100644 frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/SimpleXMLDecoder.as diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/http/AbstractOperation.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/http/AbstractOperation.as index e37a2529cb..fc4926a176 100644 --- a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/http/AbstractOperation.as +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/http/AbstractOperation.as @@ -44,6 +44,7 @@ package mx.rpc.http import mx.rpc.Fault; import mx.rpc.events.FaultEvent; import mx.rpc.mxml.Concurrency; + import mx.rpc.xml.SimpleXMLDecoder; import mx.utils.ObjectProxy; import mx.utils.ObjectUtil; import mx.utils.StringUtil; @@ -1037,79 +1038,81 @@ package mx.rpc.http { if (/*resultFormat == RESULT_FORMAT_XML || */resultFormat == RESULT_FORMAT_OBJECT || resultFormat == RESULT_FORMAT_ARRAY) - {/* - //old XML style - var tmp:Object = new XMLDocument(); - XMLDocument(tmp).ignoreWhite = true; - try - { - XMLDocument(tmp).parseXML(String(body)); - } - catch(parseError:Error) - { - var fault:Fault = new Fault(ERROR_DECODING, parseError.message); - dispatchRpcEvent(FaultEvent.createEvent(fault, token, message)); - return false; - } - if (resultFormat == RESULT_FORMAT_OBJECT || resultFormat == RESULT_FORMAT_ARRAY) - { - var decoded:Object; - var msg:String; - if (xmlDecode != null) - { - decoded = xmlDecode(tmp); - if (decoded == null) - { - msg = resourceManager.getString( - "rpc", "xmlDecodeReturnNull"); - var fault1:Fault = new Fault(ERROR_DECODING, msg); - dispatchRpcEvent(FaultEvent.createEvent(fault1, token, message)); - } - } - else - { - var decoder:SimpleXMLDecoder = new SimpleXMLDecoder(makeObjectsBindable); - - decoded = decoder.decodeXML(XMLNode(tmp)); - - if (decoded == null) - { - msg = resourceManager.getString( - "rpc", "defaultDecoderFailed"); - var fault2:Fault = new Fault(ERROR_DECODING, msg); - dispatchRpcEvent(FaultEvent.createEvent(fault2, token, message)); - } - } - - if (decoded == null) - { - return false; - } + { - if (makeObjectsBindable && (getQualifiedClassName(decoded) == "Object")) - { - decoded = new ObjectProxy(decoded); - } - else + try{ + var temp:XMLList = new XMLList(String(body)); + var tmp:XML = new XML(''+temp.toXMLString()+''); + } catch(parseError:Error) { - decoded = decoded; + var fault:Fault = new Fault(ERROR_DECODING, parseError.message); + dispatchRpcEvent(FaultEvent.createEvent(fault, token, message)); + return false; } - - if (resultFormat == RESULT_FORMAT_ARRAY) + if (resultFormat == RESULT_FORMAT_OBJECT || resultFormat == RESULT_FORMAT_ARRAY) { - decoded = decodeArray(decoded); - } - - _result = decoded; + var decoded:Object; + var msg:String; + if (xmlDecode != null) + { + decoded = xmlDecode(tmp); + if (decoded == null) + { + /*msg = resourceManager.getString( + "rpc", "xmlDecodeReturnNull");*/ + msg = 'XMLDecode returned null'; + var fault1:Fault = new Fault(ERROR_DECODING, msg); + dispatchRpcEvent(FaultEvent.createEvent(fault1, token, message)); + } + } + else + { + var decoder:SimpleXMLDecoder = new SimpleXMLDecoder(makeObjectsBindable); + + decoded = decoder.decodeXML(tmp); + + if (decoded == null) + { + /*msg = resourceManager.getString( + "rpc", "defaultDecoderFailed");*/ + msg = "defaultDecoderFailed"; + + var fault2:Fault = new Fault(ERROR_DECODING, msg); + dispatchRpcEvent(FaultEvent.createEvent(fault2, token, message)); + } + } + + if (decoded == null) + { + return false; + } + + if (makeObjectsBindable && false /*(getQualifiedClassName(decoded) == "Object")*/) + { + decoded = new ObjectProxy(decoded); + } + else + { + decoded = decoded; + } + + if (resultFormat == RESULT_FORMAT_ARRAY) + { + decoded = decodeArray(decoded); + } + + _result = decoded; } - else + /*else { - if (tmp.childNodes.length == 1) - { - tmp = tmp.firstChild; - } - _result = tmp; + if (tmp.children().length() == 1) + { + tmp = tmp.children()[0]; + } + // key difference here is that it will also be E4X: + _result = tmp; }*/ + } else if (resultFormat == RESULT_FORMAT_E4X) { diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/ComplexString.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/ComplexString.as new file mode 100644 index 0000000000..8eaa84639f --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/ComplexString.as @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.rpc.xml +{ + + [ExcludeClass] + + /** + * This internal utility class is used by SimpleXMLDecoder. The class is + * basically a dynamic version of the String class (other properties can be + * attached to it). + * + * When you try to get the value of a ComplexString, we attempt to convert the + * value to a number or boolean before returning it. + * + * @private + */ + internal dynamic class ComplexString + { + public var value:String; + + public function ComplexString(val:String) + { + super(); + value = val; + } + + public function toString():String + { + return value; + } + + COMPILE::JS {override} + public function valueOf():* + { + return SimpleXMLDecoder.simpleType(value); + } + } + +} diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/SimpleXMLDecoder.as b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/SimpleXMLDecoder.as new file mode 100644 index 0000000000..b8a5a4154b --- /dev/null +++ b/frameworks/projects/MXRoyale/src/main/royale/mx/rpc/xml/SimpleXMLDecoder.as @@ -0,0 +1,240 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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. +// +//////////////////////////////////////////////////////////////////////////////// + +package mx.rpc.xml +{ + + /* import flash.xml.XMLNode; + import flash.xml.XMLNodeType;*/ + import mx.collections.ArrayCollection; + import mx.utils.ObjectProxy; + + /** + * The SimpleXMLDecoder class deserialize XML into a graph of ActionScript objects. + * Use this class when no schema information is available. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public class SimpleXMLDecoder + { + //-------------------------------------------------------------------------- + // + // Class Methods + // + //-------------------------------------------------------------------------- + + /** + * @private + */ + public static function simpleType(val:String):Object + { + var result:Object = val; + + if (val != null) + { + //return the value as a string, a boolean or a number. + //numbers that start with 0 are left as strings + //bForceObject removed since we'll take care of converting to a String or Number object later + if (val is String && String(val) == "") + { + result = val.toString(); + } + else if (isNaN(Number(val)) || (val.charAt(0) == '0') || ((val.charAt(0) == '-') && (val.charAt(1) == '0')) || val.charAt(val.length -1) == 'E') + { + var valStr:String = val.toString(); + + //Bug 101205: Also check for boolean + var valStrLC:String = valStr.toLowerCase(); + if (valStrLC == "true") + result = true; + else if (valStrLC == "false") + result = false; + else + result = valStr; + } + else + { + result = Number(val); + } + } + + return result; + } + + //-------------------------------------------------------------------------- + // + // Constructor + // + //-------------------------------------------------------------------------- + + /** + * @private + * Constructor. + */ + public function SimpleXMLDecoder(makeObjectsBindable:Boolean = false) + { + super(); + + this.makeObjectsBindable = makeObjectsBindable; + } + + //-------------------------------------------------------------------------- + // + // Methods + // + //-------------------------------------------------------------------------- + + /** + * Converts a tree of XMLNodes into a tree of ActionScript Objects. + * + * @param dataNode An XMLNode to be converted into a tree of ActionScript Objects. + * + * @return A tree of ActionScript Objects. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public function decodeXML(dataNode:XML):Object + { + var result:Object; + var isSimpleType:Boolean = false; + + if (dataNode == null) + return null; + + // Cycle through the subnodes + var children:XMLList = dataNode.children(); + if (children.length() == 1 && children[0].nodeKind() == 'text') + { + // If exactly one text node subtype, we must want a simple + // value. + isSimpleType = true; + result = SimpleXMLDecoder.simpleType(children[0].toString()); + } + else if (children.length() > 0) + { + result = {}; + if (makeObjectsBindable) + result = new ObjectProxy(result); + + for (var i:uint = 0; i < children.length(); i++) + { + var partNode:XML = children[i]; + + // skip text nodes, which are part of mixed content + if (partNode.nodeKind() != 'element') + { + continue; + } + + var partName:String = getLocalName(partNode); + var partObj:Object = decodeXML(partNode); + + // Enable processing multiple copies of the same element (sequences) + var existing:Object = result[partName]; + if (existing != null) + { + if (existing is Array) + { + existing.push(partObj); + } + else if (existing is ArrayCollection) + { + existing.source.push(partObj); + } + else + { + existing = [existing]; + existing.push(partObj); + + if (makeObjectsBindable) + existing = new ArrayCollection(existing as Array); + + result[partName] = existing; + } + } + else + { + result[partName] = partObj; + } + } + } + + // Cycle through the attributes + var attributes:XMLList = dataNode.attributes(); + for each (var attribute:XML in attributes) + { + /* if (attribute == "xmlns" || attribute.indexOf("xmlns:") != -1) + continue;*/ + + // result can be null if it contains no children. + if (result == null) + { + result = {}; + if (makeObjectsBindable) + result = new ObjectProxy(result); + } + + // If result is not currently an Object (it is a Number, Boolean, + // or String), then convert it to be a ComplexString so that we + // can attach attributes to it. (See comment in ComplexString.as) + if (isSimpleType && !(result is ComplexString)) + { + result = new ComplexString(result.toString()); + isSimpleType = false; + } + + result[attribute.localName()] = SimpleXMLDecoder.simpleType(attribute.toString()); + } + + return result; + } + + /** + * Returns the local name of an XMLNode. + * + * @param xmlNode The XMLNode. + * + * @return The local name of an XMLNode. + * + * @langversion 3.0 + * @playerversion Flash 9 + * @playerversion AIR 1.1 + * @productversion Flex 3 + */ + public static function getLocalName(xml:XML):String + { + var name:String = xml.localName().toString(); + /*var myPrefixIndex:int = name.indexOf(":"); + if (myPrefixIndex != -1) + { + name = name.substring(myPrefixIndex+1); + }*/ + return name; + } + + private var makeObjectsBindable:Boolean; + } + +} diff --git a/frameworks/projects/XML/src/main/royale/XMLList.as b/frameworks/projects/XML/src/main/royale/XMLList.as index 7b877f1bbc..bd1bf45f22 100644 --- a/frameworks/projects/XML/src/main/royale/XMLList.as +++ b/frameworks/projects/XML/src/main/royale/XMLList.as @@ -23,6 +23,11 @@ package { import org.apache.royale.debugging.throwError; + /** + * regex to match the xml declaration + */ + private static const xmlDecl:RegExp = /^\s*<\?xml[^?]*\?>/im; + /** * mimics the top-level XMLList function (supports 'this' correctly) @@ -67,6 +72,8 @@ package { if (typeof(expression) === "string") { + var decl:String = xmlDecl.exec(expression); + if (decl) expression = expression.replace(decl,''); // try adding a wrapping node and then grab the children expression = "" + expression + ""; try