1 /* 2 * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 5 * associated documentation files (the "Software"), to deal in the Software without restriction, including 6 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 8 * following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in all copies or substantial 11 * portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 14 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 15 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 16 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 17 * USE OR OTHER DEALINGS IN THE SOFTWARE. 18 */ 19 20 package org.ksoap2.serialization; 21 22 import org.ksoap2.SoapEnvelope; 23 import org.ksoap2.SoapFault; 24 import org.ksoap2.SoapFault12; 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 import org.xmlpull.v1.XmlSerializer; 28 29 import java.io.IOException; 30 import java.util.Hashtable; 31 import java.util.Vector; 32 import java.io.ByteArrayOutputStream; 33 34 import org.kxml2.io.*; 35 36 /** 37 * @author Stefan Haustein 38 * 39 * This class extends the SoapEnvelope with Soap Serialization functionality. 40 */ 41 public class SoapSerializationEnvelope extends SoapEnvelope 42 { 43 protected static final int QNAME_TYPE = 1; 44 protected static final int QNAME_NAMESPACE = 0; 45 protected static final int QNAME_MARSHAL = 3; 46 private static final String ANY_TYPE_LABEL = "anyType"; 47 private static final String ARRAY_MAPPING_NAME = "Array"; 48 private static final String NULL_LABEL = "null"; 49 private static final String NIL_LABEL = "nil"; 50 private static final String HREF_LABEL = "href"; 51 private static final String ID_LABEL = "id"; 52 private static final String ROOT_LABEL = "root"; 53 private static final String TYPE_LABEL = "type"; 54 private static final String ITEM_LABEL = "item"; 55 private static final String ARRAY_TYPE_LABEL = "arrayType"; 56 static final Marshal DEFAULT_MARSHAL = new DM(); 57 public Hashtable properties = new Hashtable(); 58 59 Hashtable idMap = new Hashtable(); 60 Vector multiRef; // = new Vector(); 61 62 /** 63 * Set this variable to true if you don't want that type definitions for complex types/objects 64 * are automatically generated (with type "anyType") in the XML-Request, if you don't call the 65 * Method addMapping. This is needed by some Servers which have problems with these type-definitions. 66 */ 67 public boolean implicitTypes; 68 69 /** 70 * Set this variable to true for compatibility with what seems to be the default encoding for 71 * .Net-Services. This feature is an extremely ugly hack. A much better option is to change the 72 * configuration of the .Net-Server to standard Soap Serialization! 73 */ 74 75 public boolean dotNet; 76 77 /** 78 * Set this variable to true if you prefer to silently skip unknown properties. 79 * {@link RuntimeException} will be thrown otherwise. 80 */ 81 public boolean avoidExceptionForUnknownProperty; 82 83 /** 84 * Map from XML qualified names to Java classes 85 */ 86 87 protected Hashtable qNameToClass = new Hashtable(); 88 89 /** 90 * Map from Java class names to XML name and namespace pairs 91 */ 92 93 protected Hashtable classToQName = new Hashtable(); 94 95 /** 96 * Set to true to add and ID and ROOT label to the envelope. Change to false for compatibility with WSDL. 97 */ 98 protected boolean addAdornments = true; 99 SoapSerializationEnvelope(int version)100 public SoapSerializationEnvelope(int version) 101 { 102 super(version); 103 addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS); 104 DEFAULT_MARSHAL.register(this); 105 } 106 107 /** 108 * @return the addAdornments 109 */ isAddAdornments()110 public boolean isAddAdornments() 111 { 112 return addAdornments; 113 } 114 115 /** 116 * @param addAdornments 117 * the addAdornments to set 118 */ setAddAdornments(boolean addAdornments)119 public void setAddAdornments(boolean addAdornments) 120 { 121 this.addAdornments = addAdornments; 122 } 123 124 /** 125 * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut will 126 * cause #writeBody to skip writing anything redundant. 127 * @param emptyBody 128 * @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77" 129 */ setBodyOutEmpty(boolean emptyBody)130 public void setBodyOutEmpty(boolean emptyBody) { 131 if (emptyBody) { 132 bodyOut = null; 133 } 134 } 135 parseBody(XmlPullParser parser)136 public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException 137 { 138 bodyIn = null; 139 parser.nextTag(); 140 if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env) 141 && parser.getName().equals("Fault")) { 142 SoapFault fault; 143 if (this.version < SoapEnvelope.VER12) { 144 fault = new SoapFault(this.version); 145 } else { 146 fault = new SoapFault12(this.version); 147 } 148 fault.parse(parser); 149 bodyIn = fault; 150 } else { 151 while (parser.getEventType() == XmlPullParser.START_TAG) { 152 String rootAttr = parser.getAttributeValue(enc, ROOT_LABEL); 153 154 Object o = read(parser, null, -1, parser.getNamespace(), parser.getName(), 155 PropertyInfo.OBJECT_TYPE); 156 if ("1".equals(rootAttr) || bodyIn == null) { 157 bodyIn = o; 158 } 159 parser.nextTag(); 160 } 161 } 162 } 163 164 /** Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. */ readSerializable(XmlPullParser parser, SoapObject obj)165 protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException, 166 XmlPullParserException 167 { 168 for (int counter = 0; counter < parser.getAttributeCount(); counter++) { 169 String attributeName = parser.getAttributeName(counter); 170 String value = parser.getAttributeValue(counter); 171 ((SoapObject) obj).addAttribute(attributeName, value); 172 } 173 readSerializable(parser, (KvmSerializable) obj); 174 } 175 176 /** Read a KvmSerializable. */ readSerializable(XmlPullParser parser, KvmSerializable obj)177 protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException, 178 XmlPullParserException 179 { 180 while (parser.nextTag() != XmlPullParser.END_TAG) { 181 String name = parser.getName(); 182 if (!implicitTypes || !(obj instanceof SoapObject)) { 183 PropertyInfo info = new PropertyInfo(); 184 int propertyCount = obj.getPropertyCount(); 185 boolean propertyFound = false; 186 187 for (int i = 0; i < propertyCount && !propertyFound; i++) { 188 info.clear(); 189 obj.getPropertyInfo(i, properties, info); 190 191 if ((name.equals(info.name) && info.namespace == null) 192 || 193 (name.equals(info.name) && parser.getNamespace().equals(info.namespace))) { 194 propertyFound = true; 195 obj.setProperty(i, read(parser, obj, i, null, null, info)); 196 } 197 } 198 199 if (!propertyFound) { 200 if (avoidExceptionForUnknownProperty) { 201 // Dummy loop to read until corresponding END tag 202 while (parser.next() != XmlPullParser.END_TAG 203 || !name.equals(parser.getName())) { 204 } 205 ; 206 } else { 207 throw new RuntimeException("Unknown Property: " + name); 208 } 209 } 210 } else { 211 // I can only make this work for SoapObjects - hence the check above 212 // I don't understand namespaces well enough to know whether it is correct in the next line... 213 ((SoapObject) obj).addProperty(parser.getName(), 214 read(parser, obj, obj.getPropertyCount(), 215 ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE)); 216 } 217 } 218 parser.require(XmlPullParser.END_TAG, null, null); 219 } 220 221 /** 222 * If the type of the object cannot be determined, and thus no Marshal class can handle the object, this 223 * method is called. It will build either a SoapPrimitive or a SoapObject 224 * 225 * @param parser 226 * @param typeNamespace 227 * @param typeName 228 * @return unknownObject wrapped as a SoapPrimitive or SoapObject 229 * @throws IOException 230 * @throws XmlPullParserException 231 */ 232 readUnknown(XmlPullParser parser, String typeNamespace, String typeName)233 protected Object readUnknown(XmlPullParser parser, String typeNamespace, String typeName) 234 throws IOException, XmlPullParserException { 235 String name = parser.getName(); 236 String namespace = parser.getNamespace(); 237 238 // cache the attribute info list from the current element before we move on 239 Vector attributeInfoVector = new Vector(); 240 for (int attributeCount = 0; attributeCount < parser.getAttributeCount(); attributeCount++) { 241 AttributeInfo attributeInfo = new AttributeInfo(); 242 attributeInfo.setName(parser.getAttributeName(attributeCount)); 243 attributeInfo.setValue(parser.getAttributeValue(attributeCount)); 244 attributeInfo.setNamespace(parser.getAttributeNamespace(attributeCount)); 245 attributeInfo.setType(parser.getAttributeType(attributeCount)); 246 attributeInfoVector.addElement(attributeInfo); 247 } 248 249 parser.next(); // move to text, inner start tag or end tag 250 Object result = null; 251 String text = null; 252 if (parser.getEventType() == XmlPullParser.TEXT) { 253 text = parser.getText(); 254 SoapPrimitive sp = new SoapPrimitive(typeNamespace, typeName, text); 255 result = sp; 256 // apply all the cached attribute info list before we add the property and descend further for parsing 257 for (int i = 0; i < attributeInfoVector.size(); i++) { 258 sp.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 259 } 260 parser.next(); 261 } else if (parser.getEventType() == XmlPullParser.END_TAG) { 262 SoapObject so = new SoapObject(typeNamespace, typeName); 263 // apply all the cached attribute info list before we add the property and descend further for parsing 264 for (int i = 0; i < attributeInfoVector.size(); i++) { 265 so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 266 } 267 result = so; 268 } 269 270 if (parser.getEventType() == XmlPullParser.START_TAG) { 271 if (text != null && text.trim().length() != 0) { 272 throw new RuntimeException("Malformed input: Mixed content"); 273 } 274 SoapObject so = new SoapObject(typeNamespace, typeName); 275 // apply all the cached attribute info list before we add the property and descend further for parsing 276 for (int i = 0; i < attributeInfoVector.size(); i++) { 277 so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i)); 278 } 279 280 while (parser.getEventType() != XmlPullParser.END_TAG) { 281 so.addProperty(parser.getName(), 282 read(parser, so, so.getPropertyCount(), null, null, 283 PropertyInfo.OBJECT_TYPE)); 284 parser.nextTag(); 285 } 286 result = so; 287 } 288 parser.require(XmlPullParser.END_TAG, namespace, name); 289 return result; 290 } 291 getIndex(String value, int start, int dflt)292 private int getIndex(String value, int start, int dflt) { 293 if (value == null) { 294 return dflt; 295 } 296 return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1, 297 value.length() - 1)); 298 } 299 readVector(XmlPullParser parser, Vector v, PropertyInfo elementType)300 protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) 301 throws IOException, 302 XmlPullParserException { 303 String namespace = null; 304 String name = null; 305 int size = v.size(); 306 boolean dynamic = true; 307 String type = parser.getAttributeValue(enc, ARRAY_TYPE_LABEL); 308 if (type != null) { 309 int cut0 = type.indexOf(':'); 310 int cut1 = type.indexOf("[", cut0); 311 name = type.substring(cut0 + 1, cut1); 312 String prefix = cut0 == -1 ? "" : type.substring(0, cut0); 313 namespace = parser.getNamespace(prefix); 314 size = getIndex(type, cut1, -1); 315 if (size != -1) { 316 v.setSize(size); 317 dynamic = false; 318 } 319 } 320 if (elementType == null) { 321 elementType = PropertyInfo.OBJECT_TYPE; 322 } 323 parser.nextTag(); 324 int position = getIndex(parser.getAttributeValue(enc, "offset"), 0, 0); 325 while (parser.getEventType() != XmlPullParser.END_TAG) { 326 // handle position 327 position = getIndex(parser.getAttributeValue(enc, "position"), 0, position); 328 if (dynamic && position >= size) { 329 size = position + 1; 330 v.setSize(size); 331 } 332 // implicit handling of position exceeding specified size 333 v.setElementAt(read(parser, v, position, namespace, name, elementType), position); 334 position++; 335 parser.nextTag(); 336 } 337 parser.require(XmlPullParser.END_TAG, null, null); 338 } 339 340 /** 341 * Builds an object from the XML stream. This method is public for usage in conjuction with Marshal 342 * subclasses. Precondition: On the start tag of the object or property, so href can be read. 343 */ 344 read(XmlPullParser parser, Object owner, int index, String namespace, String name, PropertyInfo expected)345 public Object read(XmlPullParser parser, Object owner, int index, String namespace, 346 String name, 347 PropertyInfo expected) throws IOException, XmlPullParserException { 348 String elementName = parser.getName(); 349 String href = parser.getAttributeValue(null, HREF_LABEL); 350 Object obj; 351 if (href != null) { 352 if (owner == null) { 353 throw new RuntimeException("href at root level?!?"); 354 } 355 href = href.substring(1); 356 obj = idMap.get(href); 357 if (obj == null || obj instanceof FwdRef) { 358 FwdRef f = new FwdRef(); 359 f.next = (FwdRef) obj; 360 f.obj = owner; 361 f.index = index; 362 idMap.put(href, f); 363 obj = null; 364 } 365 parser.nextTag(); // start tag 366 parser.require(XmlPullParser.END_TAG, null, elementName); 367 } else { 368 String nullAttr = parser.getAttributeValue(xsi, NIL_LABEL); 369 String id = parser.getAttributeValue(null, ID_LABEL); 370 if (nullAttr == null) { 371 nullAttr = parser.getAttributeValue(xsi, NULL_LABEL); 372 } 373 if (nullAttr != null && SoapEnvelope.stringToBoolean(nullAttr)) { 374 obj = null; 375 parser.nextTag(); 376 parser.require(XmlPullParser.END_TAG, null, elementName); 377 } else { 378 String type = parser.getAttributeValue(xsi, TYPE_LABEL); 379 if (type != null) { 380 int cut = type.indexOf(':'); 381 name = type.substring(cut + 1); 382 String prefix = cut == -1 ? "" : type.substring(0, cut); 383 namespace = parser.getNamespace(prefix); 384 } else if (name == null && namespace == null) { 385 if (parser.getAttributeValue(enc, ARRAY_TYPE_LABEL) != null) { 386 namespace = enc; 387 name = ARRAY_MAPPING_NAME; 388 } else { 389 Object[] names = getInfo(expected.type, null); 390 namespace = (String) names[0]; 391 name = (String) names[1]; 392 } 393 } 394 // be sure to set this flag if we don't know the types. 395 if (type == null) { 396 implicitTypes = true; 397 } 398 obj = readInstance(parser, namespace, name, expected); 399 if (obj == null) { 400 obj = readUnknown(parser, namespace, name); 401 } 402 } 403 // finally, care about the id.... 404 if (id != null) { 405 Object hlp = idMap.get(id); 406 if (hlp instanceof FwdRef) { 407 FwdRef f = (FwdRef) hlp; 408 do { 409 if (f.obj instanceof KvmSerializable) { 410 ((KvmSerializable) f.obj).setProperty(f.index, obj); 411 } else { 412 ((Vector) f.obj).setElementAt(obj, f.index); 413 } 414 f = f.next; 415 } while (f != null); 416 } else if (hlp != null) { 417 throw new RuntimeException("double ID"); 418 } 419 idMap.put(id, obj); 420 } 421 } 422 423 parser.require(XmlPullParser.END_TAG, null, elementName); 424 return obj; 425 } 426 427 /** 428 * Returns a new object read from the given parser. If no mapping is found, null is returned. This method 429 * is used by the SoapParser in order to convert the XML code to Java objects. 430 */ readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)431 public Object readInstance(XmlPullParser parser, String namespace, String name, 432 PropertyInfo expected) 433 throws IOException, XmlPullParserException { 434 Object obj = qNameToClass.get(new SoapPrimitive(namespace, name, null)); 435 if (obj == null) { 436 return null; 437 } 438 if (obj instanceof Marshal) { 439 return ((Marshal) obj).readInstance(parser, namespace, name, expected); 440 } else if (obj instanceof SoapObject) { 441 obj = ((SoapObject) obj).newInstance(); 442 } else if (obj == SoapObject.class) { 443 obj = new SoapObject(namespace, name); 444 } else { 445 try { 446 obj = ((Class) obj).newInstance(); 447 } catch (Exception e) { 448 throw new RuntimeException(e.toString()); 449 } 450 } 451 // ok, obj is now the instance, fill it.... 452 if (obj instanceof SoapObject) { 453 readSerializable(parser, (SoapObject) obj); 454 } else if (obj instanceof KvmSerializable) { 455 readSerializable(parser, (KvmSerializable) obj); 456 } else if (obj instanceof Vector) { 457 readVector(parser, (Vector) obj, expected.elementType); 458 } else { 459 throw new RuntimeException("no deserializer for " + obj.getClass()); 460 } 461 return obj; 462 } 463 464 /** 465 * Returns a string array containing the namespace, name, id and Marshal object for the given java object. 466 * This method is used by the SoapWriter in order to map Java objects to the corresponding SOAP section 467 * five XML code. 468 */ getInfo(Object type, Object instance)469 public Object[] getInfo(Object type, Object instance) { 470 if (type == null) { 471 if (instance instanceof SoapObject || instance instanceof SoapPrimitive) { 472 type = instance; 473 } else { 474 type = instance.getClass(); 475 } 476 } 477 if (type instanceof SoapObject) { 478 SoapObject so = (SoapObject) type; 479 return new Object[] { 480 so.getNamespace(), so.getName(), null, null 481 }; 482 } 483 if (type instanceof SoapPrimitive) { 484 SoapPrimitive sp = (SoapPrimitive) type; 485 return new Object[] { 486 sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL 487 }; 488 } 489 if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) { 490 Object[] tmp = (Object[]) classToQName.get(((Class) type).getName()); 491 if (tmp != null) { 492 return tmp; 493 } 494 } 495 return new Object[] { 496 xsd, ANY_TYPE_LABEL, null, null 497 }; 498 } 499 500 /** 501 * Defines a direct mapping from a namespace and name to a java class (and vice versa), using the given 502 * marshal mechanism 503 */ addMapping(String namespace, String name, Class clazz, Marshal marshal)504 public void addMapping(String namespace, String name, Class clazz, Marshal marshal) { 505 qNameToClass 506 .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz 507 : marshal); 508 classToQName.put(clazz.getName(), new Object[] { 509 namespace, name, null, marshal 510 }); 511 } 512 513 /** 514 * Defines a direct mapping from a namespace and name to a java class (and vice versa) 515 */ addMapping(String namespace, String name, Class clazz)516 public void addMapping(String namespace, String name, Class clazz) { 517 addMapping(namespace, name, clazz, null); 518 } 519 520 /** 521 * Adds a SoapObject to the class map. During parsing, objects of the given type (namespace/name) will be 522 * mapped to corresponding copies of the given SoapObject, maintaining the structure of the template. 523 */ addTemplate(SoapObject so)524 public void addTemplate(SoapObject so) { 525 qNameToClass.put(new SoapPrimitive(so.namespace, so.name, null), so); 526 } 527 528 /** 529 * Response from the soap call. Pulls the object from the wrapper object and returns it. 530 * 531 * @since 2.0.3 532 * @return response from the soap call. 533 * @throws SoapFault 534 */ getResponse()535 public Object getResponse() throws SoapFault { 536 if (bodyIn instanceof SoapFault) { 537 throw (SoapFault) bodyIn; 538 } 539 KvmSerializable ks = (KvmSerializable) bodyIn; 540 541 if (ks.getPropertyCount() == 0) { 542 return null; 543 } else if (ks.getPropertyCount() == 1) { 544 return ks.getProperty(0); 545 } else { 546 Vector ret = new Vector(); 547 for (int i = 0; i < ks.getPropertyCount(); i++) { 548 ret.add(ks.getProperty(i)); 549 } 550 return ret; 551 } 552 } 553 554 /** 555 * Serializes the request object to the given XmlSerliazer object 556 * 557 * @param writer 558 * XmlSerializer object to write the body into. 559 */ writeBody(XmlSerializer writer)560 public void writeBody(XmlSerializer writer) throws IOException { 561 // allow an empty body without any tags in it 562 // see http://code.google.com/p/ksoap2-android/issues/detail?id=77 563 if (bodyOut != null) { 564 multiRef = new Vector(); 565 multiRef.addElement(bodyOut); 566 Object[] qName = getInfo(null, bodyOut); 567 writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 568 (String) qName[QNAME_TYPE]); //<spp:sppPostDevData 569 if (dotNet) { 570 writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]); 571 } 572 if (addAdornments) { 573 writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]); 574 writer.attribute(enc, ROOT_LABEL, "1"); 575 } 576 writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); //.... 577 writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 578 (String) qName[QNAME_TYPE]);//</spp:sppPostDevData> 579 } 580 } 581 582 /** 583 * Writes the body of an SoapObject. This method write the attributes and then calls 584 * "writeObjectBody (writer, (KvmSerializable)obj);" 585 */ writeObjectBody(XmlSerializer writer, SoapObject obj)586 public void writeObjectBody(XmlSerializer writer, SoapObject obj) throws IOException { 587 SoapObject soapObject = (SoapObject) obj; 588 int cnt = soapObject.getAttributeCount(); 589 for (int counter = 0; counter < cnt; counter++) { 590 AttributeInfo attributeInfo = new AttributeInfo(); 591 soapObject.getAttributeInfo(counter, attributeInfo); 592 writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), attributeInfo 593 .getValue() 594 .toString()); 595 } 596 writeObjectBody(writer, (KvmSerializable) obj); 597 } 598 599 /** 600 * Writes the body of an KvmSerializable object. This method is public for access from Marshal subclasses. 601 */ writeObjectBody(XmlSerializer writer, KvmSerializable obj)602 public void writeObjectBody(XmlSerializer writer, KvmSerializable obj) throws IOException { 603 int cnt = obj.getPropertyCount(); 604 PropertyInfo propertyInfo = new PropertyInfo(); 605 String namespace; 606 String name; 607 String type; 608 for (int i = 0; i < cnt; i++) { 609 // get the property 610 Object prop = obj.getProperty(i); 611 // and importantly also get the property info which holds the name potentially! 612 obj.getPropertyInfo(i, properties, propertyInfo); 613 614 if (!(prop instanceof SoapObject)) { 615 // prop is a PropertyInfo 616 if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) { 617 writer.startTag(propertyInfo.namespace, propertyInfo.name); 618 writeProperty(writer, obj.getProperty(i), propertyInfo); 619 writer.endTag(propertyInfo.namespace, propertyInfo.name); 620 } 621 } else { 622 // prop is a SoapObject 623 SoapObject nestedSoap = (SoapObject) prop; 624 // lets get the info from the soap object itself 625 Object[] qName = getInfo(null, nestedSoap); 626 namespace = (String) qName[QNAME_NAMESPACE]; 627 type = (String) qName[QNAME_TYPE]; 628 629 // prefer the name from the property info 630 if (propertyInfo.name != null && propertyInfo.name.length() > 0) { 631 name = propertyInfo.name; 632 } else { 633 name = (String) qName[QNAME_TYPE]; 634 } 635 636 // treat MO data as CDATA 637 if (name.equals("DevInfo") || name.equals("DevDetail") 638 || name.equals("PerProviderSubscription") || // format v4 639 name.equals("MgmtTree") // format v6 640 ) { 641 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 642 XmlSerializer xw = new KXmlSerializer(); 643 xw.setOutput(bos, "UTF-8"); 644 xw.startTag((dotNet) ? "" : namespace, name); 645 if (!implicitTypes) { 646 String prefix = writer.getPrefix(namespace, true); 647 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 648 } 649 writeObjectBody(xw, nestedSoap); 650 xw.endTag((dotNet) ? "" : namespace, name); 651 xw.flush(); 652 //bos.write('\r'); 653 //bos.write('\n'); 654 bos.flush(); 655 writer.cdsect(bos.toString()); 656 } 657 else 658 { 659 writer.startTag((dotNet) ? "" : namespace, name); 660 if (!implicitTypes) { 661 String prefix = writer.getPrefix(namespace, true); 662 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 663 } 664 writeObjectBody(writer, nestedSoap); 665 writer.endTag((dotNet) ? "" : namespace, name); 666 } 667 } 668 } 669 } 670 writeProperty(XmlSerializer writer, Object obj, PropertyInfo type)671 protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) 672 throws IOException { 673 if (obj == null) { 674 ///M: Modify for HS20 675 //writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true"); 676 return; 677 } 678 Object[] qName = getInfo(null, obj); 679 if (type.multiRef || qName[2] != null) { 680 int i = multiRef.indexOf(obj); 681 if (i == -1) { 682 i = multiRef.size(); 683 multiRef.addElement(obj); 684 } 685 writer.attribute(null, HREF_LABEL, qName[2] == null ? ("#o" + i) : "#" + qName[2]); 686 } else { 687 if (!implicitTypes || obj.getClass() != type.type) { 688 String prefix = writer.getPrefix((String) qName[QNAME_NAMESPACE], true); 689 writer.attribute(xsi, TYPE_LABEL, prefix + ":" + qName[QNAME_TYPE]); 690 } 691 writeElement(writer, obj, type, qName[QNAME_MARSHAL]); 692 } 693 } 694 writeElement(XmlSerializer writer, Object element, PropertyInfo type, Object marshal)695 private void writeElement(XmlSerializer writer, Object element, PropertyInfo type, 696 Object marshal) 697 throws IOException { 698 if (marshal != null) { 699 ((Marshal) marshal).writeInstance(writer, element); 700 } else if (element instanceof SoapObject) { 701 writeObjectBody(writer, (SoapObject) element); 702 } else if (element instanceof KvmSerializable) { 703 writeObjectBody(writer, (KvmSerializable) element); 704 } else if (element instanceof Vector) { 705 writeVectorBody(writer, (Vector) element, type.elementType); 706 } else { 707 throw new RuntimeException("Cannot serialize: " + element); 708 } 709 } 710 writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType)711 protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType) 712 throws IOException { 713 String itemsTagName = ITEM_LABEL; 714 String itemsNamespace = null; 715 716 if (elementType == null) { 717 elementType = PropertyInfo.OBJECT_TYPE; 718 } else if (elementType instanceof PropertyInfo) { 719 if (elementType.name != null) { 720 itemsTagName = elementType.name; 721 itemsNamespace = elementType.namespace; 722 } 723 } 724 725 int cnt = vector.size(); 726 Object[] arrType = getInfo(elementType.type, null); 727 728 // This removes the arrayType attribute from the xml for arrays(required for most .Net services to work) 729 if (!implicitTypes) { 730 writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false) 731 + ":" 732 + arrType[1] + "[" + cnt + "]"); 733 } 734 735 boolean skipped = false; 736 for (int i = 0; i < cnt; i++) { 737 if (vector.elementAt(i) == null) { 738 skipped = true; 739 } else { 740 writer.startTag(itemsNamespace, itemsTagName); 741 if (skipped) { 742 writer.attribute(enc, "position", "[" + i + "]"); 743 skipped = false; 744 } 745 writeProperty(writer, vector.elementAt(i), elementType); 746 writer.endTag(itemsNamespace, itemsTagName); 747 } 748 } 749 } 750 } 751