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 20package org.ksoap2.serialization; 21 22import org.ksoap2.SoapEnvelope; 23import org.ksoap2.SoapFault; 24import org.ksoap2.SoapFault12; 25import org.xmlpull.v1.XmlPullParser; 26import org.xmlpull.v1.XmlPullParserException; 27import org.xmlpull.v1.XmlSerializer; 28 29import java.io.IOException; 30import java.util.Hashtable; 31import java.util.Vector; 32import java.io.ByteArrayOutputStream; 33 34import org.kxml2.io.*; 35 36/** 37 * @author Stefan Haustein 38 * 39 * This class extends the SoapEnvelope with Soap Serialization functionality. 40 */ 41public 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 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 */ 110 public boolean isAddAdornments() 111 { 112 return addAdornments; 113 } 114 115 /** 116 * @param addAdornments 117 * the addAdornments to set 118 */ 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 */ 130 public void setBodyOutEmpty(boolean emptyBody) { 131 if (emptyBody) { 132 bodyOut = null; 133 } 134 } 135 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. */ 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. */ 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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