• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From b5b548a4be9f1ced6f5599b62765216f9ab8af00 Mon Sep 17 00:00:00 2001
2From: Ecco Park <eccopark@google.com>
3Date: Tue, 8 May 2018 13:44:14 -0700
4Subject: [PATCH 1/1] ksoap2 update
5
6Change-Id: Iace4c0f3cb31c9532c5fa0c44c2dc863bd81b23e
7Signed-off-by: Ecco Park <eccopark@google.com>
8---
9 .../main/java/org/ksoap2/SoapEnvelope.java    |  17 +-
10 .../src/main/java/org/ksoap2/SoapFault12.java |  13 +-
11 .../serialization/AttributeContainer.java     | 128 +++++-
12 .../java/org/ksoap2/serialization/DM.java     |  57 ++-
13 .../ksoap2/serialization/HasAttributes.java   |  16 +
14 .../ksoap2/serialization/HasInnerText.java    |  17 +
15 .../ksoap2/serialization/KvmSerializable.java |  25 +-
16 .../org/ksoap2/serialization/Marshal.java     |   3 +-
17 .../ksoap2/serialization/MarshalBase64.java   |   3 +-
18 .../org/ksoap2/serialization/MarshalDate.java |   3 +-
19 .../serialization/MarshalHashtable.java       |   8 +-
20 .../org/ksoap2/serialization/SoapObject.java  | 340 +++++++++++++-
21 .../ksoap2/serialization/SoapPrimitive.java   |  20 +-
22 .../SoapSerializationEnvelope.java            | 419 ++++++++++++------
23 .../org/ksoap2/serialization/ValueWriter.java |  13 +
24 .../ksoap2/transport/ServiceConnection.java   |  13 +-
25 .../java/org/ksoap2/transport/Transport.java  | 131 ++++--
26 .../ksoap2/serialization/MarshalFloat.java    |   3 +-
27 .../transport/HttpResponseException.java      |  60 +++
28 .../org/ksoap2/transport/HttpTransportSE.java | 348 +++++++--------
29 .../transport/HttpsServiceConnectionSE.java   |  58 ++-
30 .../ksoap2/transport/HttpsTransportSE.java    |  81 ++--
31 .../transport/KeepAliveHttpsTransportSE.java  |  20 +-
32 .../ksoap2/transport/ServiceConnectionSE.java |  44 +-
33 24 files changed, 1298 insertions(+), 542 deletions(-)
34 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
35 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
36 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
37 create mode 100644 ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
38
39diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
40index 8a0b894..1c43656 100644
41--- a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
42+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
43@@ -54,10 +54,8 @@ public class SoapEnvelope {
44     /** Namespace constant: http://www.w3.org/1999/XMLSchema */
45     public static final String XSI1999 = "http://www.w3.org/1999/XMLSchema-instance";
46
47-    //public static final String NS20 = "http://www.wi-fi-org/specifications/hotspot2dot0/spp/1.0/";
48     public static final String NS20 = "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp";
49
50-    //public static final String OMADM12 = "http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd";
51
52     /**
53      * Returns true for the string values "1" and "true", ignoring upper/lower
54@@ -105,9 +103,8 @@ public class SoapEnvelope {
55     /** Xml Schema data namespace, set by the constructor */
56     public String xsd;
57
58-    ///M: HS20 Add by Jungo
59+    // HS20 change
60     public String ns;
61-    public String omadm;
62
63     /**
64      * Initializes a SOAP Envelope. The version parameter must be set to one of
65@@ -129,10 +126,8 @@ public class SoapEnvelope {
66             enc = SoapEnvelope.ENC2003;
67             env = SoapEnvelope.ENV2003;
68         }
69-
70+        // HS20 change
71         ns = SoapEnvelope.NS20;
72-        //omadm = SoapEnvelope.OMADM12;
73-
74     }
75
76     /** Parses the SOAP envelope from the given parser */
77@@ -206,13 +201,9 @@ public class SoapEnvelope {
78      * given XML writer.
79      */
80     public void write(XmlSerializer writer) throws IOException {
81-        ///M: HS20 modify by Jungo
82-        //writer.setPrefix("i", xsi);
83-        //writer.setPrefix("d", xsd);
84-        //writer.setPrefix("c", enc);
85-        writer.setPrefix("soap", env);//the prefix for namespace env in xml output
86+        // HS 2.0 changes
87+        writer.setPrefix("soap", env); //the prefix for namespace env in xml output
88         writer.setPrefix("spp", ns);
89-        //writer.setPrefix("omadm", omadm);
90
91         writer.startTag(env, "Envelope");
92         writer.startTag(env, "Header");
93diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
94index 5667cb4..3f39147 100644
95--- a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
96+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
97@@ -72,27 +72,28 @@ public class SoapFault12 extends SoapFault {
98
99         while (parser.nextTag() == XmlPullParser.START_TAG) {
100             String name = parser.getName();
101+            String namespace = parser.getNamespace();
102             parser.nextTag();
103-            if (name.equals("Code")) {
104+            if (name.toLowerCase().equals("Code".toLowerCase())) {
105                 this.Code = new Node();
106                 this.Code.parse(parser);
107-            } else if (name.equals("Reason")) {
108+            } else if (name.toLowerCase().equals("Reason".toLowerCase())) {
109                 this.Reason = new Node();
110                 this.Reason.parse(parser);
111-            } else if (name.equals("Node")) {
112+            } else if (name.toLowerCase().equals("Node".toLowerCase())) {
113                 this.Node = new Node();
114                 this.Node.parse(parser);
115-            } else if (name.equals("Role")) {
116+            } else if (name.toLowerCase().equals("Role".toLowerCase())) {
117                 this.Role = new Node();
118                 this.Role.parse(parser);
119-            } else if (name.equals("Detail")) {
120+            } else if (name.toLowerCase().equals("Detail".toLowerCase())) {
121                 this.Detail = new Node();
122                 this.Detail.parse(parser);
123             } else {
124                 throw new RuntimeException("unexpected tag:" + name);
125             }
126
127-            parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, name);
128+            parser.require(XmlPullParser.END_TAG, namespace, name);
129         }
130         parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, "Fault");
131         parser.nextTag();
132diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
133index 6b83847..34d2723 100644
134--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
135+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
136@@ -3,8 +3,8 @@ package org.ksoap2.serialization;
137
138 import java.util.Vector;
139
140-public class AttributeContainer {
141-    private Vector attributes = new Vector();
142+public class AttributeContainer implements HasAttributes{
143+    protected Vector attributes = new Vector();
144
145     /**
146      * Places AttributeInfo of desired attribute into a designated AttributeInfo object
147@@ -29,9 +29,9 @@ public class AttributeContainer {
148         return ((AttributeInfo) attributes.elementAt(index)).getValue();
149     }
150
151-    /**
152-    * Get the attribute's toString value.
153-    */
154+     /**
155+     * Get the attribute's toString value.
156+     */
157     public String getAttributeAsString(int index) {
158         AttributeInfo attributeInfo = (AttributeInfo) attributes.elementAt(index);
159         return attributeInfo.getValue().toString();
160@@ -51,6 +51,20 @@ public class AttributeContainer {
161         }
162     }
163
164+    /**
165+     * Get the attribute with the given name
166+     *
167+     * @throws RuntimeException if the attribute does not exist
168+     */
169+    public Object getAttribute(String namespace,String name) {
170+        Integer i = attributeIndex(namespace,name);
171+        if (i != null) {
172+            return getAttribute(i.intValue());
173+        } else {
174+            throw new RuntimeException("illegal property: " + name);
175+        }
176+    }
177+
178     /**
179      * Get the toString value of the attribute with the given name.
180      *
181@@ -65,6 +79,19 @@ public class AttributeContainer {
182         }
183     }
184
185+    /**
186+     * Get the toString value of the attribute with the given name.
187+     *
188+     * @throws RuntimeException if the attribute does not exist
189+     */
190+    public String getAttributeAsString(String namespace,String name) {
191+        Integer i = attributeIndex(namespace,name);
192+        if (i != null) {
193+            return getAttribute(i.intValue()).toString();
194+        } else {
195+            throw new RuntimeException("illegal property: " + name);
196+        }
197+    }
198     /**
199      * Knows whether the given attribute exists
200      */
201@@ -76,6 +103,16 @@ public class AttributeContainer {
202         }
203     }
204
205+    /**
206+     * Knows whether the given attribute exists
207+     */
208+    public boolean hasAttribute(final String namespace,final String name) {
209+        if (attributeIndex(namespace,name) != null) {
210+            return true;
211+        } else {
212+            return false;
213+        }
214+    }
215     /**
216      * Get an attribute without chance of throwing an exception
217      *
218@@ -91,6 +128,21 @@ public class AttributeContainer {
219         }
220     }
221
222+    /**
223+     * Get an attribute without chance of throwing an exception
224+     *
225+     * @param name the name of the attribute to retrieve
226+     * @return the value of the attribute if it exists; {@code null} if it does not exist
227+     */
228+    public Object getAttributeSafely(String namespace,String name) {
229+        Integer i = attributeIndex(namespace,name);
230+        if (i != null) {
231+            return getAttribute(i.intValue());
232+        } else {
233+            return null;
234+        }
235+    }
236+
237     /**
238      * Get an attributes' toString value without chance of throwing an
239      * exception.
240@@ -108,6 +160,23 @@ public class AttributeContainer {
241         }
242     }
243
244+    /**
245+     * Get an attributes' toString value without chance of throwing an
246+     * exception.
247+
248+     * @param name
249+     * @return the value of the attribute,s toString method if it exists; ""
250+     * if it does not exist
251+     */
252+    public Object getAttributeSafelyAsString(String namespace,String name) {
253+        Integer i = attributeIndex(namespace,name);
254+        if (i != null) {
255+            return getAttribute(i.intValue()).toString();
256+        } else {
257+            return "";
258+        }
259+    }
260+
261     private Integer attributeIndex(String name) {
262         for (int i = 0; i < attributes.size(); i++) {
263             if (name.equals(((AttributeInfo) attributes.elementAt(i)).getName())) {
264@@ -117,6 +186,16 @@ public class AttributeContainer {
265         return null;
266     }
267
268+    private Integer attributeIndex(String namespace,String name) {
269+        for (int i = 0; i < attributes.size(); i++) {
270+            AttributeInfo attrInfo=(AttributeInfo) attributes.elementAt(i);
271+            if (name.equals(attrInfo.getName()) && namespace.equals(attrInfo.getNamespace())) {
272+                return new Integer(i);
273+            }
274+        }
275+        return null;
276+    }
277+
278     /**
279      * Returns the number of attributes
280      *
281@@ -160,13 +239,25 @@ public class AttributeContainer {
282      * @return {@code this} object.
283      */
284     public void addAttribute(String name, Object value) {
285+        addAttribute(null,name,value);
286+    }
287+
288+    /**
289+     * Adds a attribute (parameter) to the object.
290+     *
291+     * @param namespace  The namespace of the attribute
292+     * @param name  The name of the attribute
293+     * @param value the value of the attribute
294+     * @return {@code this} object.
295+     */
296+    public void addAttribute(String namespace,String name, Object value) {
297         AttributeInfo attributeInfo = new AttributeInfo();
298         attributeInfo.name = name;
299+        attributeInfo.namespace = namespace;
300         attributeInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass();
301         attributeInfo.value = value;
302         addAttribute(attributeInfo);
303     }
304-
305     /**
306      * Add an attribute if the value is not null.
307      * @param name
308@@ -178,6 +269,18 @@ public class AttributeContainer {
309         }
310     }
311
312+    /**
313+     * Add an attribute if the value is not null.
314+     * @param namespace  The namespace of the attribute
315+     * @param name
316+     * @param value
317+     */
318+    public void addAttributeIfValue(String namespace,String name, Object value) {
319+        if (value != null) {
320+            addAttribute(namespace,name, value);
321+        }
322+    }
323+
324     /**
325      * Add a new attribute by providing an {@link AttributeInfo} object.  {@code AttributeInfo}
326      * contains all data about the attribute, including name and value.}
327@@ -198,4 +301,17 @@ public class AttributeContainer {
328             attributes.addElement(attributeInfo);
329         }
330     }
331+
332+
333+    public void setAttribute(AttributeInfo info) {
334+
335+
336+    }
337+
338+
339+    public void getAttribute(int index, AttributeInfo info) {
340+
341+
342+    }
343+
344 }
345diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
346index 78d4449..255126e 100644
347--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
348+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
349@@ -20,9 +20,12 @@
350
351 package org.ksoap2.serialization;
352
353-import java.io.*;
354-import org.xmlpull.v1.*;
355-import org.ksoap2.*;
356+import java.io.IOException;
357+
358+import org.ksoap2.SoapEnvelope;
359+import org.xmlpull.v1.XmlPullParser;
360+import org.xmlpull.v1.XmlPullParserException;
361+import org.xmlpull.v1.XmlSerializer;
362
363 /**
364  * This class is not public, so save a few bytes by using a short class name (DM
365@@ -30,8 +33,7 @@ import org.ksoap2.*;
366  */
367 class DM implements Marshal {
368
369-    public Object readInstance(XmlPullParser parser, String namespace, String name,
370-            PropertyInfo expected)
371+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo excepted)
372             throws IOException, XmlPullParserException {
373         String text = parser.nextText();
374         switch (name.charAt(0)) {
375@@ -49,9 +51,10 @@ class DM implements Marshal {
376     }
377
378     /**
379-     * Write the instance out. In case it is an AttributeContainer write those our first though.
380-     * @param writer
381-     *            the xml serializer.
382+     * Write the instance out. In case it is an AttributeContainer write those our first though.
383+     * If it HasAttributes then write the attributes and values.
384+     *
385+     * @param writer   the xml serializer.
386      * @param instance
387      * @throws IOException
388      */
389@@ -62,11 +65,43 @@ class DM implements Marshal {
390             for (int counter = 0; counter < cnt; counter++) {
391                 AttributeInfo attributeInfo = new AttributeInfo();
392                 attributeContainer.getAttributeInfo(counter, attributeInfo);
393-                writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
394-                        attributeInfo.getValue().toString());
395+                try {
396+                    attributeContainer.getAttribute(counter, attributeInfo);
397+                } catch (Exception e) {
398+                    e.printStackTrace();
399+                }
400+                if (attributeInfo.getValue() != null) {
401+                    writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
402+                            (attributeInfo.getValue() != null) ? attributeInfo.getValue().toString() : "");
403+                }
404+            }
405+        } else if (instance instanceof HasAttributes) {
406+            HasAttributes soapObject = (HasAttributes) instance;
407+            int cnt = soapObject.getAttributeCount();
408+            for (int counter = 0; counter < cnt; counter++) {
409+                AttributeInfo attributeInfo = new AttributeInfo();
410+                soapObject.getAttributeInfo(counter, attributeInfo);
411+                try {
412+                    soapObject.getAttribute(counter, attributeInfo);
413+                } catch (Exception e) {
414+                    e.printStackTrace();
415+                }
416+                if (attributeInfo.getValue() != null) {
417+                    writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
418+                            attributeInfo.getValue() != null ? attributeInfo.getValue().toString() : "");
419+                }
420             }
421         }
422-        writer.text(instance.toString());
423+
424+        if(instance  instanceof ValueWriter)
425+        {
426+            ((ValueWriter)instance).write(writer);
427+        }
428+        else
429+        {
430+            writer.text(instance.toString());
431+        }
432+
433     }
434
435     public void register(SoapSerializationEnvelope cm) {
436diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
437new file mode 100644
438index 0000000..b513138
439--- /dev/null
440+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
441@@ -0,0 +1,16 @@
442+package org.ksoap2.serialization;
443+
444+/**
445+ * Common inteface for classes which want to serialize attributes to outgoing soap message
446+ *
447+ * @author robocik
448+ */
449+public interface HasAttributes {
450+    int getAttributeCount();
451+
452+    void getAttributeInfo(int index, AttributeInfo info);
453+
454+    void getAttribute(int index, AttributeInfo info);
455+
456+    void setAttribute(AttributeInfo info);
457+}
458diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
459new file mode 100644
460index 0000000..b35c35b
461--- /dev/null
462+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
463@@ -0,0 +1,17 @@
464+package org.ksoap2.serialization;
465+/**
466+ * Interface for classes requiring inner text of xml  tags
467+ *
468+ * @author satansly
469+ */
470+public interface HasInnerText {
471+     /**
472+     * Gets the inner text of xml tags
473+     */
474+    Object getInnerText();
475+
476+    /**
477+     * @param s String to be set as inner text for an outgoing soap object
478+     */
479+    void setInnerText(Object s);
480+}
481diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
482index bded0c0..09d7b32 100644
483--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
484+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
485@@ -39,31 +39,26 @@ public interface KvmSerializable {
486      */
487     Object getProperty(int index);
488
489-    /**
490-     * @return the number of serializable properties
491+    /**
492+     * @return the number of serializable properties
493      */
494     int getPropertyCount();
495
496     /**
497      * Sets the property with the given index to the given value.
498-     *
499-     * @param index
500-     *            the index to be set
501-     * @param value
502-     *            the value of the property
503+     *
504+     * @param index the index to be set
505+     * @param value the value of the property
506      */
507     void setProperty(int index, Object value);
508
509     /**
510      * Fills the given property info record.
511-     *
512-     * @param index
513-     *            the index to be queried
514-     * @param properties
515-     *            information about the (de)serializer.  Not frequently used.
516-     * @param info
517-     *            The return parameter, to be filled with information about the
518-     *            property with the given index.
519+     *
520+     * @param index      the index to be queried
521+     * @param properties information about the (de)serializer.  Not frequently used.
522+     * @param info       The return parameter, to be filled with information about the
523+     *                   property with the given index.
524      */
525     void getPropertyInfo(int index, Hashtable properties, PropertyInfo info);
526
527diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
528index cfa9d81..100f107 100644
529--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
530+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
531@@ -41,8 +41,7 @@ public interface Marshal {
532      *            the namespace.
533      * @return the object read from the xml stream.
534      */
535-    public Object readInstance(XmlPullParser parser, String namespace, String name,
536-            PropertyInfo expected)
537+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
538             throws IOException, XmlPullParserException;
539
540     /**
541diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
542index 2f8420c..2239027 100644
543--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
544+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
545@@ -31,8 +31,7 @@ import org.xmlpull.v1.*;
546 public class MarshalBase64 implements Marshal {
547     public static Class BYTE_ARRAY_CLASS = new byte[0].getClass();
548
549-    public Object readInstance(XmlPullParser parser, String namespace, String name,
550-            PropertyInfo expected)
551+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
552             throws IOException, XmlPullParserException {
553         return Base64.decode(parser.nextText());
554     }
555diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
556index 3e4fa06..489ba3b 100644
557--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
558+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
559@@ -31,8 +31,7 @@ import org.ksoap2.kobjects.isodate.*;
560 public class MarshalDate implements Marshal {
561     public static Class DATE_CLASS = new Date().getClass();
562
563-    public Object readInstance(XmlPullParser parser, String namespace, String name,
564-            PropertyInfo expected)
565+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
566             throws IOException, XmlPullParserException {
567         return IsoDate.stringToDate(parser.nextText(), IsoDate.DATE_TIME);
568     }
569diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
570index d2367e9..0c6b53e 100644
571--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
572+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
573@@ -46,8 +46,7 @@ public class MarshalHashtable implements Marshal {
574     public static final Class HASHTABLE_CLASS = new Hashtable().getClass();
575     SoapSerializationEnvelope envelope;
576
577-    public Object readInstance(XmlPullParser parser, String namespace, String name,
578-            PropertyInfo expected)
579+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
580             throws IOException, XmlPullParserException {
581         Hashtable instance = new Hashtable();
582         String elementName = parser.getName();
583@@ -81,7 +80,7 @@ public class MarshalHashtable implements Marshal {
584             Object key = keys.nextElement();
585             item.setProperty(0, key);
586             item.setProperty(1, h.get(key));
587-            envelope.writeObjectBody(writer, item);
588+            envelope.writeObjectBodyWithAttributes(writer, item);
589             writer.endTag("", "item");
590         }
591     }
592@@ -89,7 +88,6 @@ public class MarshalHashtable implements Marshal {
593     class ItemSoapObject extends SoapObject {
594         Hashtable h;
595         int resolvedIndex = -1;
596-
597         ItemSoapObject(Hashtable h) {
598             super(null, null);
599             this.h = h;
600@@ -107,7 +105,7 @@ public class MarshalHashtable implements Marshal {
601                 Object resolved = resolvedIndex == 0 ? getProperty(0) : getProperty(1);
602                 if (index == 0) {
603                     h.put(value, resolved);
604-                } else {
605+                } else  {
606                     h.put(resolved, value);
607                 }
608             }
609diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
610index 24a1ffe..f11210a 100644
611--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
612+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
613@@ -38,7 +38,7 @@ import java.util.*;
614  * KvmSerializable interface.
615  */
616
617-public class SoapObject extends AttributeContainer implements KvmSerializable {
618+public class SoapObject extends AttributeContainer implements KvmSerializable, HasInnerText {
619
620     private static final String EMPTY_STRING = "";
621     /**
622@@ -54,6 +54,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
623      */
624     protected Vector properties = new Vector();
625
626+    protected Object innerText;
627+
628     // TODO: accessing properties and attributes would work much better if we
629     // kept a list of known properties instead of iterating through the list
630     // each time
631@@ -181,6 +183,230 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
632         }
633     }
634
635+    /**
636+     * Get the property with the given name
637+     *
638+     * return null
639+     *             if the property does not exist
640+     */
641+    public Object getProperty(String namespace,String name) {
642+        Integer index = propertyIndex(namespace,name);
643+        if (index != null) {
644+            return getProperty(index.intValue());
645+        }
646+        else {
647+            throw new RuntimeException("illegal property: " + name);
648+        }
649+    }
650+
651+    /**
652+     * Get a property using namespace and name without chance of throwing an exception
653+     *
654+     * @return the property if it exists; if not, {@link NullSoapObject} is
655+     *         returned
656+     */
657+    public Object getPropertyByNamespaceSafely(final String namespace, final String name) {
658+        Integer i = propertyIndex(namespace,name);
659+        if (i != null) {
660+            return getProperty(i.intValue());
661+        } else {
662+            return new NullSoapObject();
663+        }
664+    }
665+
666+    /**
667+     * Get the toString value of a property without chance of throwing an
668+     * exception
669+     *
670+     * @return the string value of the property if it exists; if not, #EMPTY_STRING is
671+     *         returned
672+     */
673+    public String getPropertyByNamespaceSafelyAsString(final String namespace,final String name) {
674+        Integer i = propertyIndex(namespace,name);
675+        if (i != null) {
676+            Object foo = getProperty(i.intValue());
677+            if (foo == null) {
678+                return EMPTY_STRING;
679+            } else {
680+                return foo.toString();
681+            }
682+        } else {
683+            return EMPTY_STRING;
684+        }
685+    }
686+
687+    /**
688+     * Get a property without chance of throwing an exception. An object can be
689+     * provided to this method; if the property is not found, this object will
690+     * be returned.
691+     *
692+     * @param defaultThing
693+     *            the object to return if the property is not found
694+     * @return the property if it exists; defaultThing if the property does not
695+     *         exist
696+     */
697+    public Object getPropertySafely(final String namespace,final String name, final Object defaultThing) {
698+        Integer i = propertyIndex(namespace,name);
699+        if (i != null) {
700+            return getProperty(i.intValue());
701+        } else {
702+            return defaultThing;
703+        }
704+    }
705+
706+    /**
707+     * Get the toString value of a property without chance of throwing an
708+     * exception. An object can be provided to this method; if the property is
709+     * not found, this object's string representation will be returned.
710+     *
711+     * @param defaultThing
712+     *            toString of the object to return if the property is not found
713+     * @return the property toString if it exists; defaultThing toString if the
714+     *         property does not exist, if the defaultThing is null #EMPTY_STRING
715+     *         is returned
716+     */
717+    public String getPropertySafelyAsString(final String namespace,final String name,
718+                                            final Object defaultThing) {
719+        Integer i = propertyIndex(namespace,name);
720+        if (i != null) {
721+            Object property = getProperty(i.intValue());
722+            if (property != null) {
723+                return property.toString();
724+            } else {
725+                return EMPTY_STRING;
726+            }
727+        } else {
728+            if (defaultThing != null) {
729+                return defaultThing.toString();
730+            } else {
731+                return EMPTY_STRING;
732+            }
733+        }
734+    }
735+
736+    /**
737+     * Get the primitive property with the given name.
738+     *
739+     * @param name
740+     * @return PropertyInfo containing an empty string if property either complex or empty
741+     */
742+    public Object getPrimitiveProperty(final String namespace,final String name){
743+        Integer index = propertyIndex(namespace,name);
744+        if (index != null){
745+            PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
746+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
747+                return propertyInfo.getValue();
748+            } else {
749+                propertyInfo = new PropertyInfo();
750+                propertyInfo.setType(String.class);
751+                propertyInfo.setValue(EMPTY_STRING);
752+                propertyInfo.setName(name);
753+                propertyInfo.setNamespace(namespace);
754+                return (Object) propertyInfo.getValue();
755+            }
756+        } else {
757+            throw new RuntimeException("illegal property: " + name);
758+        }
759+    }
760+
761+    /**
762+     * Get the toString value of the primitive property with the given name.
763+     * Returns empty string if property either complex or empty
764+     *
765+     * @param name
766+     * @return the string value of the property
767+     */
768+    public String getPrimitivePropertyAsString(final String namespace,final String name){
769+        Integer index = propertyIndex(namespace,name);
770+        if (index != null){
771+            PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
772+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
773+                return propertyInfo.getValue().toString();
774+            } else {
775+                return EMPTY_STRING;
776+            }
777+        } else {
778+            throw new RuntimeException("illegal property: " + name);
779+        }
780+    }
781+
782+    /**
783+     * Get the toString value of a primitive property without chance of throwing an
784+     * exception
785+     *
786+     * @param name
787+     * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
788+     *         returned
789+     */
790+    public Object getPrimitivePropertySafely(final String namespace,final String name) {
791+        Integer index = propertyIndex(namespace,name);
792+        if (index != null){
793+            PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
794+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
795+                return propertyInfo.getValue().toString();
796+            } else {
797+                propertyInfo = new PropertyInfo();
798+                propertyInfo.setType(String.class);
799+                propertyInfo.setValue(EMPTY_STRING);
800+                propertyInfo.setName(name);
801+                propertyInfo.setNamespace(namespace);
802+                return (Object) propertyInfo.getValue();
803+            }
804+        } else {
805+            return new NullSoapObject();
806+        }
807+    }
808+
809+    /**
810+     * Get the toString value of a primitive property without chance of throwing an
811+     * exception
812+     *
813+     * @param name
814+     * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
815+     *         returned
816+     */
817+    public String getPrimitivePropertySafelyAsString(final String namespace,final String name) {
818+        Integer index = propertyIndex(namespace,name);
819+        if (index != null){
820+            PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
821+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
822+                return propertyInfo.getValue().toString();
823+            } else {
824+                return EMPTY_STRING;
825+            }
826+        } else {
827+            return EMPTY_STRING;
828+        }
829+    }
830+
831+    /**
832+     * Knows whether the given property exists
833+     */
834+    public boolean hasProperty(final String namespace,final String name) {
835+        if (propertyIndex(namespace,name) != null) {
836+            return true;
837+        } else {
838+            return false;
839+        }
840+    }
841+
842+    /**
843+     * Get the toString value of the property.
844+     *
845+     * @param namespace
846+     * @param name
847+     * @return
848+     */
849+
850+    public String getPropertyAsString(String namespace,String name) {
851+        Integer index = propertyIndex(namespace,name);
852+        if (index != null) {
853+            return getProperty(index.intValue()).toString();
854+        } else {
855+            throw new RuntimeException("illegal property: " + name);
856+        }
857+    }
858+
859     /**
860      * Get the toString value of the property.
861      *
862@@ -301,9 +527,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
863      */
864     public Object getPrimitiveProperty(final String name) {
865         Integer index = propertyIndex(name);
866-        if (index != null) {
867+        if (index != null){
868             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
869-            if (propertyInfo.getType() != SoapObject.class) {
870+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
871                 return propertyInfo.getValue();
872             } else {
873                 propertyInfo = new PropertyInfo();
874@@ -326,9 +552,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
875      */
876     public String getPrimitivePropertyAsString(final String name) {
877         Integer index = propertyIndex(name);
878-        if (index != null) {
879+        if (index != null){
880             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
881-            if (propertyInfo.getType() != SoapObject.class) {
882+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
883                 return propertyInfo.getValue().toString();
884             } else {
885                 return EMPTY_STRING;
886@@ -348,9 +574,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
887      */
888     public Object getPrimitivePropertySafely(final String name) {
889         Integer index = propertyIndex(name);
890-        if (index != null) {
891+        if (index != null){
892             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
893-            if (propertyInfo.getType() != SoapObject.class) {
894+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
895                 return propertyInfo.getValue().toString();
896             } else {
897                 propertyInfo = new PropertyInfo();
898@@ -374,9 +600,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
899      */
900     public String getPrimitivePropertySafelyAsString(final String name) {
901         Integer index = propertyIndex(name);
902-        if (index != null) {
903+        if (index != null){
904             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
905-            if (propertyInfo.getType() != SoapObject.class) {
906+            if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
907                 return propertyInfo.getValue().toString();
908             } else {
909                 return EMPTY_STRING;
910@@ -397,6 +623,18 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
911         return null;
912     }
913
914+
915+    private Integer propertyIndex(String namespace,String name) {
916+        if (name != null && namespace!=null) {
917+            for (int i = 0; i < properties.size(); i++) {
918+                PropertyInfo info= (PropertyInfo) properties.elementAt(i);
919+                if (name.equals(info.getName()) && namespace.equals(info.getNamespace())) {
920+                    return new Integer(i);
921+                }
922+            }
923+        }
924+        return null;
925+    }
926     /**
927      * Returns the number of properties
928      *
929@@ -453,6 +691,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
930         }
931     }
932
933+    public PropertyInfo getPropertyInfo(int index) {
934+        Object element = properties.elementAt(index);
935+        if (element instanceof PropertyInfo) {
936+            PropertyInfo p = (PropertyInfo) element;
937+            return p;
938+        } else {
939+            // SoapObject
940+            return null;
941+        }
942+    }
943+
944     /**
945      * Creates a new SoapObject based on this, allows usage of SoapObjects as
946      * templates. One application is to set the expected return type of a soap
947@@ -466,17 +715,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
948             Object prop = properties.elementAt(propIndex);
949             if (prop instanceof PropertyInfo) {
950                 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(propIndex);
951-                PropertyInfo propertyInfoClonned = (PropertyInfo) propertyInfo.clone();
952-                o.addProperty(propertyInfoClonned);
953-            } else if (prop instanceof SoapObject) {
954-                o.addSoapObject(((SoapObject) prop).newInstance());
955+                PropertyInfo propertyInfoClonned = (PropertyInfo)propertyInfo.clone();
956+                o.addProperty( propertyInfoClonned );
957+            } else if(prop instanceof SoapObject) {
958+                o.addSoapObject(((SoapObject)prop).newInstance());
959             }
960         }
961         for (int attribIndex = 0; attribIndex < getAttributeCount(); attribIndex++) {
962             AttributeInfo newAI = new AttributeInfo();
963             getAttributeInfo(attribIndex, newAI);
964             AttributeInfo attributeInfo = newAI; // (AttributeInfo)
965-                                                 // attributes.elementAt(attribIndex);
966+                                                    // attributes.elementAt(attribIndex);
967             o.addAttribute(attributeInfo);
968         }
969         return o;
970@@ -513,11 +762,49 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
971         propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
972                 .getClass();
973         propertyInfo.value = value;
974+        return addProperty(propertyInfo);
975+    }
976+
977+    /**
978+     * Adds a property (parameter) to the object. This is essentially a sub
979+     * element.
980+     *
981+     * @param namespace
982+     *            The namespace of the property
983+     * @param name
984+     *            The name of the property
985+     * @param value
986+     *            the value of the property
987+     */
988+    public SoapObject addProperty(String namespace,String name, Object value) {
989+        PropertyInfo propertyInfo = new PropertyInfo();
990+        propertyInfo.name = name;
991         propertyInfo.namespace = namespace;
992-        ///M: HS20 modify by Jungo
993+        propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
994+                .getClass();
995+        propertyInfo.value = value;
996         return addProperty(propertyInfo);
997     }
998
999+    /**
1000+     * Add a property only if the value is not null.
1001+     *
1002+     * @param namespace
1003+     *            The namespace of the property
1004+     * @param name
1005+     *            The name of the property
1006+     * @param value
1007+     *            the value of the property
1008+     * @return
1009+     */
1010+    public SoapObject addPropertyIfValue(String namespace,String name, Object value) {
1011+        if (value != null) {
1012+            return addProperty(namespace,name, value);
1013+        } else {
1014+            return this;
1015+        }
1016+    }
1017+
1018     /**
1019      * Add a property only if the value is not null.
1020      *
1021@@ -597,12 +884,12 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
1022         StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{");
1023         for (int i = 0; i < getPropertyCount(); i++) {
1024             Object prop = properties.elementAt(i);
1025-            if (prop instanceof PropertyInfo) {
1026+            if(prop instanceof PropertyInfo) {
1027                 buf.append(EMPTY_STRING)
1028-                        .append(((PropertyInfo) prop).getName())
1029-                        .append("=")
1030-                        .append(getProperty(i))
1031-                        .append("; ");
1032+                    .append(((PropertyInfo) prop).getName())
1033+                    .append("=")
1034+                    .append(getProperty(i))
1035+                    .append("; ");
1036             } else {
1037                 buf.append(((SoapObject) prop).toString());
1038             }
1039@@ -610,4 +897,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
1040         buf.append("}");
1041         return buf.toString();
1042     }
1043+    public Object getInnerText() {
1044+         return innerText;
1045+    }
1046+
1047+    public void setInnerText(Object innerText)
1048+    {
1049+        this.innerText=innerText;
1050+    }
1051+
1052+    public void removePropertyInfo(Object info)
1053+    {
1054+        properties.remove(info);
1055+    }
1056 }
1057diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
1058index d09f7a7..32fe333 100644
1059--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
1060+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
1061@@ -34,11 +34,14 @@ package org.ksoap2.serialization;
1062  */
1063
1064 public class SoapPrimitive extends AttributeContainer {
1065-    String namespace;
1066-    String name;
1067-    String value;
1068+    protected String namespace;
1069+    protected String name;
1070+    protected Object value;
1071
1072-    public SoapPrimitive(String namespace, String name, String value) {
1073+    public  static final Object NullSkip = new Object();
1074+    public  static final Object NullNilElement = new Object();
1075+
1076+    public SoapPrimitive(String namespace, String name, Object value) {
1077         this.namespace = namespace;
1078         this.name = name;
1079         this.value = value;
1080@@ -50,7 +53,7 @@ public class SoapPrimitive extends AttributeContainer {
1081         }
1082         SoapPrimitive p = (SoapPrimitive) o;
1083         boolean varsEqual = name.equals(p.name)
1084-                && (namespace == null ? p.namespace == null : namespace.equals(p.namespace))
1085+                && (namespace == null ? p.namespace == null:namespace.equals(p.namespace))
1086                 && (value == null ? (p.value == null) : value.equals(p.value));
1087         return varsEqual && attributesAreEqual(p);
1088     }
1089@@ -60,7 +63,7 @@ public class SoapPrimitive extends AttributeContainer {
1090     }
1091
1092     public String toString() {
1093-        return value;
1094+        return value != null ? value.toString() : null;
1095     }
1096
1097     public String getNamespace() {
1098@@ -70,4 +73,9 @@ public class SoapPrimitive extends AttributeContainer {
1099     public String getName() {
1100         return name;
1101     }
1102+
1103+    public Object getValue() {
1104+        return value;
1105+    }
1106+
1107 }
1108diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
1109index dae09d2..ceeb3f4 100644
1110--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
1111+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
1112@@ -27,9 +27,9 @@ import org.xmlpull.v1.XmlPullParserException;
1113 import org.xmlpull.v1.XmlSerializer;
1114
1115 import java.io.IOException;
1116+import java.util.ArrayList;
1117 import java.util.Hashtable;
1118 import java.util.Vector;
1119-import java.io.ByteArrayOutputStream;
1120
1121 import org.kxml2.io.*;
1122
1123@@ -43,29 +43,29 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1124     protected static final int QNAME_TYPE = 1;
1125     protected static final int QNAME_NAMESPACE = 0;
1126     protected static final int QNAME_MARSHAL = 3;
1127+    protected static final String NULL_LABEL = "null";
1128+    protected static final String NIL_LABEL = "nil";
1129+    static final Marshal DEFAULT_MARSHAL = new DM();
1130     private static final String ANY_TYPE_LABEL = "anyType";
1131     private static final String ARRAY_MAPPING_NAME = "Array";
1132-    private static final String NULL_LABEL = "null";
1133-    private static final String NIL_LABEL = "nil";
1134     private static final String HREF_LABEL = "href";
1135     private static final String ID_LABEL = "id";
1136     private static final String ROOT_LABEL = "root";
1137     private static final String TYPE_LABEL = "type";
1138     private static final String ITEM_LABEL = "item";
1139     private static final String ARRAY_TYPE_LABEL = "arrayType";
1140-    static final Marshal DEFAULT_MARSHAL = new DM();
1141     public Hashtable properties = new Hashtable();
1142-
1143-    Hashtable idMap = new Hashtable();
1144-    Vector multiRef; // = new Vector();
1145-
1146     /**
1147      * Set this variable to true if you don't want that type definitions for complex types/objects
1148      * are automatically generated (with type "anyType") in the XML-Request, if you don't call the
1149      * Method addMapping. This is needed by some Servers which have problems with these type-definitions.
1150      */
1151     public boolean implicitTypes;
1152-
1153+    /**
1154+     * If set to true then all properties with null value will be skipped from the soap message.
1155+     * If false then null properties will be sent as <element nil="true" />
1156+     */
1157+    public boolean skipNullProperties;
1158     /**
1159      * Set this variable to true for compatibility with what seems to be the default encoding for
1160      * .Net-Services. This feature is an extremely ugly hack. A much better option is to change the
1161@@ -96,9 +96,10 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1162      * Set to true to add and ID and ROOT label to the envelope. Change to false for compatibility with WSDL.
1163      */
1164     protected boolean addAdornments = true;
1165+    Hashtable idMap = new Hashtable();
1166+    Vector multiRef; // = new Vector();
1167
1168-    public SoapSerializationEnvelope(int version)
1169-    {
1170+    public SoapSerializationEnvelope(int version) {
1171         super(version);
1172         addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS);
1173         DEFAULT_MARSHAL.register(this);
1174@@ -107,23 +108,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1175     /**
1176      * @return the addAdornments
1177      */
1178-    public boolean isAddAdornments()
1179-    {
1180+    public boolean isAddAdornments() {
1181         return addAdornments;
1182     }
1183
1184     /**
1185-     * @param addAdornments
1186-     *            the addAdornments to set
1187+     * @param addAdornments the addAdornments to set
1188      */
1189-    public void setAddAdornments(boolean addAdornments)
1190-    {
1191+    public void setAddAdornments(boolean addAdornments) {
1192         this.addAdornments = addAdornments;
1193     }
1194
1195     /**
1196      * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut will
1197      * cause #writeBody to skip writing anything redundant.
1198+     *
1199      * @param emptyBody
1200      * @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77"
1201      */
1202@@ -133,8 +132,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1203         }
1204     }
1205
1206-    public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException
1207-    {
1208+    public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException {
1209         bodyIn = null;
1210         parser.nextTag();
1211         if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env)
1212@@ -161,10 +159,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1213         }
1214     }
1215
1216-    /** Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. */
1217+    /**
1218+     * Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable.
1219+     */
1220     protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException,
1221-            XmlPullParserException
1222-    {
1223+            XmlPullParserException {
1224         for (int counter = 0; counter < parser.getAttributeCount(); counter++) {
1225             String attributeName = parser.getAttributeName(counter);
1226             String value = parser.getAttributeValue(counter);
1227@@ -173,11 +172,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1228         readSerializable(parser, (KvmSerializable) obj);
1229     }
1230
1231-    /** Read a KvmSerializable. */
1232+    /**
1233+     * Read a KvmSerializable.
1234+     */
1235     protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException,
1236-            XmlPullParserException
1237-    {
1238-        while (parser.nextTag() != XmlPullParser.END_TAG) {
1239+            XmlPullParserException {
1240+        int tag = 0;
1241+        try {
1242+            tag = parser.nextTag();
1243+        } catch (XmlPullParserException e) {
1244+            if(obj instanceof HasInnerText){
1245+                 ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
1246+            }
1247+            tag = parser.nextTag();
1248+        }
1249+        while (tag != XmlPullParser.END_TAG) {
1250             String name = parser.getName();
1251             if (!implicitTypes || !(obj instanceof SoapObject)) {
1252                 PropertyInfo info = new PropertyInfo();
1253@@ -188,8 +197,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1254                     info.clear();
1255                     obj.getPropertyInfo(i, properties, info);
1256
1257-                    if ((name.equals(info.name) && info.namespace == null)
1258-                            ||
1259+                    if ((name.equals(info.name) && info.namespace == null) ||
1260                             (name.equals(info.name) && parser.getNamespace().equals(info.namespace))) {
1261                         propertyFound = true;
1262                         obj.setProperty(i, read(parser, obj, i, null, null, info));
1263@@ -199,21 +207,42 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1264                 if (!propertyFound) {
1265                     if (avoidExceptionForUnknownProperty) {
1266                         // Dummy loop to read until corresponding END tag
1267-                        while (parser.next() != XmlPullParser.END_TAG
1268-                                || !name.equals(parser.getName())) {
1269+                        while (parser.next() != XmlPullParser.END_TAG || !name.equals(parser.getName())) {
1270                         }
1271                         ;
1272                     } else {
1273                         throw new RuntimeException("Unknown Property: " + name);
1274                     }
1275+                } else {
1276+                    if (obj instanceof HasAttributes) {
1277+                        HasAttributes soapObject = (HasAttributes) obj;
1278+                        int cnt = parser.getAttributeCount();
1279+                        for (int counter = 0; counter < cnt; counter++) {
1280+                            AttributeInfo attributeInfo = new AttributeInfo();
1281+                            attributeInfo.setName(parser.getAttributeName(counter));
1282+                            attributeInfo.setValue(parser.getAttributeValue(counter));
1283+                            attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
1284+                            attributeInfo.setType(parser.getAttributeType(counter));
1285+                            soapObject.setAttribute(attributeInfo);
1286+
1287+                        }
1288+                    }
1289                 }
1290             } else {
1291                 // I can only make this work for SoapObjects - hence the check above
1292                 // I don't understand namespaces well enough to know whether it is correct in the next line...
1293-                ((SoapObject) obj).addProperty(parser.getName(),
1294-                        read(parser, obj, obj.getPropertyCount(),
1295-                                ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE));
1296+                ((SoapObject) obj).addProperty(parser.getName(), read(parser, obj, obj.getPropertyCount(),
1297+                        ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE));
1298+            }
1299+            try {
1300+                tag = parser.nextTag();
1301+            } catch (XmlPullParserException e) {
1302+                if(obj instanceof HasInnerText){
1303+                    ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
1304+                }
1305+                tag = parser.nextTag();
1306             }
1307+
1308         }
1309         parser.require(XmlPullParser.END_TAG, null, null);
1310     }
1311@@ -278,9 +307,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1312             }
1313
1314             while (parser.getEventType() != XmlPullParser.END_TAG) {
1315-                so.addProperty(parser.getName(),
1316-                        read(parser, so, so.getPropertyCount(), null, null,
1317-                                PropertyInfo.OBJECT_TYPE));
1318+                so.addProperty(parser.getNamespace(),parser.getName(), read(parser, so, so.getPropertyCount(),
1319+                        null, null, PropertyInfo.OBJECT_TYPE));
1320                 parser.nextTag();
1321             }
1322             result = so;
1323@@ -293,12 +321,15 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1324         if (value == null) {
1325             return dflt;
1326         }
1327-        return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
1328-                value.length() - 1));
1329+        try {
1330+            return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
1331+                    value.length() - 1));
1332+        } catch (Exception ex) {
1333+            return dflt;
1334+        }
1335     }
1336
1337-    protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType)
1338-            throws IOException,
1339+    protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) throws IOException,
1340             XmlPullParserException {
1341         String namespace = null;
1342         String name = null;
1343@@ -337,14 +368,23 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1344         parser.require(XmlPullParser.END_TAG, null, null);
1345     }
1346
1347+    /**
1348+     * This method returns id from the href attribute value.
1349+     * By default we assume that href value looks like this: #id so we basically have to remove the first character.
1350+     * But in theory there could be a different value format, like cid:value, etc...
1351+     */
1352+    protected String getIdFromHref(String hrefValue) {
1353+        return hrefValue.substring(1);
1354+    }
1355+
1356     /**
1357      * Builds an object from the XML stream. This method is public for usage in conjuction with Marshal
1358      * subclasses. Precondition: On the start tag of the object or property, so href can be read.
1359      */
1360
1361-    public Object read(XmlPullParser parser, Object owner, int index, String namespace,
1362-            String name,
1363-            PropertyInfo expected) throws IOException, XmlPullParserException {
1364+    public Object read(XmlPullParser parser, Object owner, int index, String namespace, String name,
1365+            PropertyInfo expected)
1366+            throws IOException, XmlPullParserException {
1367         String elementName = parser.getName();
1368         String href = parser.getAttributeValue(null, HREF_LABEL);
1369         Object obj;
1370@@ -352,7 +392,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1371             if (owner == null) {
1372                 throw new RuntimeException("href at root level?!?");
1373             }
1374-            href = href.substring(1);
1375+            href = getIdFromHref(href);
1376             obj = idMap.get(href);
1377             if (obj == null || obj instanceof FwdRef) {
1378                 FwdRef f = new FwdRef();
1379@@ -402,21 +442,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1380             }
1381             // finally, care about the id....
1382             if (id != null) {
1383-                Object hlp = idMap.get(id);
1384-                if (hlp instanceof FwdRef) {
1385-                    FwdRef f = (FwdRef) hlp;
1386-                    do {
1387-                        if (f.obj instanceof KvmSerializable) {
1388-                            ((KvmSerializable) f.obj).setProperty(f.index, obj);
1389-                        } else {
1390-                            ((Vector) f.obj).setElementAt(obj, f.index);
1391-                        }
1392-                        f = f.next;
1393-                    } while (f != null);
1394-                } else if (hlp != null) {
1395-                    throw new RuntimeException("double ID");
1396-                }
1397-                idMap.put(id, obj);
1398+                resolveReference(id, obj);
1399+
1400             }
1401         }
1402
1403@@ -424,12 +451,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1404         return obj;
1405     }
1406
1407+    protected void resolveReference(String id, Object obj) {
1408+        Object hlp = idMap.get(id);
1409+        if (hlp instanceof FwdRef) {
1410+            FwdRef f = (FwdRef) hlp;
1411+            do {
1412+                if (f.obj instanceof KvmSerializable) {
1413+                    ((KvmSerializable) f.obj).setProperty(f.index, obj);
1414+                } else {
1415+                    ((Vector) f.obj).setElementAt(obj, f.index);
1416+                }
1417+                f = f.next;
1418+            }
1419+            while (f != null);
1420+        } else if (hlp != null) {
1421+            throw new RuntimeException("double ID");
1422+        }
1423+        idMap.put(id, obj);
1424+    }
1425+
1426     /**
1427      * Returns a new object read from the given parser. If no mapping is found, null is returned. This method
1428      * is used by the SoapParser in order to convert the XML code to Java objects.
1429      */
1430-    public Object readInstance(XmlPullParser parser, String namespace, String name,
1431-            PropertyInfo expected)
1432+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
1433             throws IOException, XmlPullParserException {
1434         Object obj = qNameToClass.get(new SoapPrimitive(namespace, name, null));
1435         if (obj == null) {
1436@@ -448,10 +493,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1437                 throw new RuntimeException(e.toString());
1438             }
1439         }
1440+        if (obj instanceof HasAttributes) {
1441+            HasAttributes soapObject = (HasAttributes) obj;
1442+            int cnt = parser.getAttributeCount();
1443+            for (int counter = 0; counter < cnt; counter++) {
1444+
1445+                AttributeInfo attributeInfo = new AttributeInfo();
1446+                attributeInfo.setName(parser.getAttributeName(counter));
1447+                attributeInfo.setValue(parser.getAttributeValue(counter));
1448+                attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
1449+                attributeInfo.setType(parser.getAttributeType(counter));
1450+
1451+                soapObject.setAttribute(attributeInfo);
1452+
1453+            }
1454+        }
1455+
1456         // ok, obj is now the instance, fill it....
1457         if (obj instanceof SoapObject) {
1458             readSerializable(parser, (SoapObject) obj);
1459         } else if (obj instanceof KvmSerializable) {
1460+
1461+            if(obj instanceof HasInnerText){
1462+                ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
1463+            }
1464             readSerializable(parser, (KvmSerializable) obj);
1465         } else if (obj instanceof Vector) {
1466             readVector(parser, (Vector) obj, expected.elementType);
1467@@ -476,15 +541,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1468         }
1469         if (type instanceof SoapObject) {
1470             SoapObject so = (SoapObject) type;
1471-            return new Object[] {
1472-                    so.getNamespace(), so.getName(), null, null
1473-            };
1474+            return new Object[]{so.getNamespace(), so.getName(), null, null};
1475         }
1476         if (type instanceof SoapPrimitive) {
1477             SoapPrimitive sp = (SoapPrimitive) type;
1478-            return new Object[] {
1479-                    sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL
1480-            };
1481+            return new Object[]{sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL};
1482         }
1483         if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) {
1484             Object[] tmp = (Object[]) classToQName.get(((Class) type).getName());
1485@@ -492,9 +553,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1486                 return tmp;
1487             }
1488         }
1489-        return new Object[] {
1490-                xsd, ANY_TYPE_LABEL, null, null
1491-        };
1492+        return new Object[]{xsd, ANY_TYPE_LABEL, null, null};
1493     }
1494
1495     /**
1496@@ -503,11 +562,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1497      */
1498     public void addMapping(String namespace, String name, Class clazz, Marshal marshal) {
1499         qNameToClass
1500-                .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz
1501-                        : marshal);
1502-        classToQName.put(clazz.getName(), new Object[] {
1503-                namespace, name, null, marshal
1504-        });
1505+                .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz : marshal);
1506+        classToQName.put(clazz.getName(), new Object[]{namespace, name, null, marshal});
1507     }
1508
1509     /**
1510@@ -528,11 +584,14 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1511     /**
1512      * Response from the soap call. Pulls the object from the wrapper object and returns it.
1513      *
1514-     * @since 2.0.3
1515      * @return response from the soap call.
1516      * @throws SoapFault
1517+     * @since 2.0.3
1518      */
1519     public Object getResponse() throws SoapFault {
1520+        if (bodyIn == null) {
1521+            return null;
1522+        }
1523         if (bodyIn instanceof SoapFault) {
1524             throw (SoapFault) bodyIn;
1525         }
1526@@ -554,8 +613,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1527     /**
1528      * Serializes the request object to the given XmlSerliazer object
1529      *
1530-     * @param writer
1531-     *            XmlSerializer object to write the body into.
1532+     * @param writer XmlSerializer object to write the body into.
1533      */
1534     public void writeBody(XmlSerializer writer) throws IOException {
1535         // allow an empty body without any tags in it
1536@@ -564,36 +622,48 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1537             multiRef = new Vector();
1538             multiRef.addElement(bodyOut);
1539             Object[] qName = getInfo(null, bodyOut);
1540-            writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
1541-                    (String) qName[QNAME_TYPE]); //<spp:sppPostDevData
1542-            if (dotNet) {
1543-                writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]);
1544-            }
1545+
1546+            writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]);
1547+
1548+                if (dotNet) {
1549+                    writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]);
1550+                }
1551+
1552             if (addAdornments) {
1553                 writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]);
1554                 writer.attribute(enc, ROOT_LABEL, "1");
1555             }
1556-            writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); //....
1557-            writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
1558-                    (String) qName[QNAME_TYPE]);//</spp:sppPostDevData>
1559+            writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]);
1560+            writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]);
1561         }
1562     }
1563
1564-    /**
1565-     * Writes the body of an SoapObject. This method write the attributes and then calls
1566-     * "writeObjectBody (writer, (KvmSerializable)obj);"
1567-     */
1568-    public void writeObjectBody(XmlSerializer writer, SoapObject obj) throws IOException {
1569-        SoapObject soapObject = (SoapObject) obj;
1570+    private void writeAttributes(XmlSerializer writer, HasAttributes obj) throws IOException {
1571+        HasAttributes soapObject = (HasAttributes) obj;
1572         int cnt = soapObject.getAttributeCount();
1573         for (int counter = 0; counter < cnt; counter++) {
1574             AttributeInfo attributeInfo = new AttributeInfo();
1575             soapObject.getAttributeInfo(counter, attributeInfo);
1576-            writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), attributeInfo
1577-                    .getValue()
1578-                    .toString());
1579+            soapObject.getAttribute(counter, attributeInfo);
1580+            if (attributeInfo.getValue() != null) {
1581+                writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
1582+                        attributeInfo.getValue().toString());
1583+            }
1584+        }
1585+    }
1586+
1587+    public void writeArrayListBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException {
1588+        if (obj instanceof HasAttributes) {
1589+            writeAttributes(writer, (HasAttributes) obj);
1590         }
1591-        writeObjectBody(writer, (KvmSerializable) obj);
1592+        writeArrayListBody(writer, (ArrayList) obj);
1593+    }
1594+
1595+    public void writeObjectBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException {
1596+        if (obj instanceof HasAttributes) {
1597+            writeAttributes(writer, (HasAttributes) obj);
1598+        }
1599+        writeObjectBody(writer, obj);
1600     }
1601
1602     /**
1603@@ -614,9 +684,12 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1604             if (!(prop instanceof SoapObject)) {
1605                 // prop is a PropertyInfo
1606                 if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
1607-                    writer.startTag(propertyInfo.namespace, propertyInfo.name);
1608-                    writeProperty(writer, obj.getProperty(i), propertyInfo);
1609-                    writer.endTag(propertyInfo.namespace, propertyInfo.name);
1610+                    Object objValue = obj.getProperty(i);
1611+                    if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) {
1612+                        writer.startTag(propertyInfo.namespace, propertyInfo.name);
1613+                        writeProperty(writer, objValue, propertyInfo);
1614+                        writer.endTag(propertyInfo.namespace, propertyInfo.name);
1615+                    }
1616                 }
1617             } else {
1618                 // prop is a SoapObject
1619@@ -633,46 +706,47 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1620                     name = (String) qName[QNAME_TYPE];
1621                 }
1622
1623-                // treat MO data as CDATA
1624-                if (name.equals("DevInfo") || name.equals("DevDetail")
1625-                        || name.equals("PerProviderSubscription") || // format v4
1626-                        name.equals("MgmtTree") // format v6
1627-                ) {
1628-                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
1629-                    XmlSerializer xw = new KXmlSerializer();
1630-                    xw.setOutput(bos, "UTF-8");
1631-                    xw.startTag((dotNet) ? "" : namespace, name);
1632-                    if (!implicitTypes) {
1633-                        String prefix = writer.getPrefix(namespace, true);
1634-                        writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
1635-                    }
1636-                    writeObjectBody(xw, nestedSoap);
1637-                    xw.endTag((dotNet) ? "" : namespace, name);
1638-                    xw.flush();
1639-                    //bos.write('\r');
1640-                    //bos.write('\n');
1641-                    bos.flush();
1642-                    writer.cdsect(bos.toString());
1643+                // prefer the namespace from the property info
1644+                if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
1645+                    namespace = propertyInfo.namespace;
1646+                } else {
1647+                    namespace = (String) qName[QNAME_NAMESPACE];
1648+                }
1649+
1650+                writer.startTag(namespace, name);
1651+                if (!implicitTypes) {
1652+                    String prefix = writer.getPrefix(namespace, true);
1653+                    writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
1654+                }
1655+                writeObjectBodyWithAttributes(writer, nestedSoap);
1656+                writer.endTag(namespace, name);
1657+            }
1658+        }
1659+        writeInnerText(writer, obj);
1660+
1661+    }
1662+
1663+    private void writeInnerText(XmlSerializer writer, KvmSerializable obj) throws IOException {
1664+        if(obj instanceof HasInnerText){
1665+
1666+            Object value=((HasInnerText)obj).getInnerText();
1667+            if (value != null) {
1668+                if(value instanceof ValueWriter)
1669+                {
1670+                    ((ValueWriter)value).write(writer);
1671                 }
1672                 else
1673                 {
1674-                    writer.startTag((dotNet) ? "" : namespace, name);
1675-                    if (!implicitTypes) {
1676-                        String prefix = writer.getPrefix(namespace, true);
1677-                        writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
1678-                    }
1679-                    writeObjectBody(writer, nestedSoap);
1680-                    writer.endTag((dotNet) ? "" : namespace, name);
1681+                    writer.cdsect(value.toString());
1682                 }
1683+
1684             }
1685         }
1686     }
1687
1688-    protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type)
1689-            throws IOException {
1690-        if (obj == null) {
1691-            ///M: Modify for HS20
1692-            //writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
1693+    protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) throws IOException {
1694+        if (obj == null || obj == SoapPrimitive.NullNilElement) {
1695+            writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
1696             return;
1697         }
1698         Object[] qName = getInfo(null, obj);
1699@@ -692,15 +766,19 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1700         }
1701     }
1702
1703-    private void writeElement(XmlSerializer writer, Object element, PropertyInfo type,
1704-            Object marshal)
1705+    protected void writeElement(XmlSerializer writer, Object element, PropertyInfo type, Object marshal)
1706             throws IOException {
1707         if (marshal != null) {
1708             ((Marshal) marshal).writeInstance(writer, element);
1709-        } else if (element instanceof SoapObject) {
1710-            writeObjectBody(writer, (SoapObject) element);
1711-        } else if (element instanceof KvmSerializable) {
1712-            writeObjectBody(writer, (KvmSerializable) element);
1713+        } else if (element instanceof KvmSerializable || element == SoapPrimitive.NullNilElement
1714+                || element == SoapPrimitive.NullSkip) {
1715+            if (element instanceof ArrayList) {
1716+                writeArrayListBodyWithAttributes(writer, (KvmSerializable) element);
1717+            } else {
1718+                writeObjectBodyWithAttributes(writer, (KvmSerializable) element);
1719+            }
1720+        } else if (element instanceof HasAttributes) {
1721+            writeAttributes(writer, (HasAttributes) element);
1722         } else if (element instanceof Vector) {
1723             writeVectorBody(writer, (Vector) element, type.elementType);
1724         } else {
1725@@ -708,6 +786,65 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1726         }
1727     }
1728
1729+    protected void writeArrayListBody(XmlSerializer writer, ArrayList list)
1730+            throws IOException {
1731+        KvmSerializable obj = (KvmSerializable) list;
1732+        int cnt = list.size();
1733+        PropertyInfo propertyInfo = new PropertyInfo();
1734+        String namespace;
1735+        String name;
1736+        String type;
1737+        for (int i = 0; i < cnt; i++) {
1738+            // get the property
1739+            Object prop = obj.getProperty(i);
1740+            // and importantly also get the property info which holds the name potentially!
1741+            obj.getPropertyInfo(i, properties, propertyInfo);
1742+
1743+            if (!(prop instanceof SoapObject)) {
1744+                // prop is a PropertyInfo
1745+                if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
1746+                    Object objValue = obj.getProperty(i);
1747+                    if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) {
1748+                        writer.startTag(propertyInfo.namespace, propertyInfo.name);
1749+                        writeProperty(writer, objValue, propertyInfo);
1750+                        writer.endTag(propertyInfo.namespace, propertyInfo.name);
1751+                    }
1752+                }
1753+            } else {
1754+
1755+                // prop is a SoapObject
1756+                SoapObject nestedSoap = (SoapObject) prop;
1757+                // lets get the info from the soap object itself
1758+                Object[] qName = getInfo(null, nestedSoap);
1759+                namespace = (String) qName[QNAME_NAMESPACE];
1760+                type = (String) qName[QNAME_TYPE];
1761+
1762+                // prefer the name from the property info
1763+                if (propertyInfo.name != null && propertyInfo.name.length() > 0) {
1764+                    name = propertyInfo.name;
1765+                } else {
1766+                    name = (String) qName[QNAME_TYPE];
1767+                }
1768+
1769+                // prefer the namespace from the property info
1770+                if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
1771+                    namespace = propertyInfo.namespace;
1772+                } else {
1773+                    namespace = (String) qName[QNAME_NAMESPACE];
1774+                }
1775+
1776+                writer.startTag(namespace, name);
1777+                if (!implicitTypes) {
1778+                    String prefix = writer.getPrefix(namespace, true);
1779+                    writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
1780+                }
1781+                writeObjectBodyWithAttributes(writer, nestedSoap);
1782+                writer.endTag(namespace, name);
1783+            }
1784+        }
1785+        writeInnerText(writer, obj);
1786+    }
1787+
1788     protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType)
1789             throws IOException {
1790         String itemsTagName = ITEM_LABEL;
1791@@ -727,9 +864,13 @@ public class SoapSerializationEnvelope extends SoapEnvelope
1792
1793         // This removes the arrayType attribute from the xml for arrays(required for most .Net services to work)
1794         if (!implicitTypes) {
1795-            writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false)
1796-                    + ":"
1797+            writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false) + ":"
1798                     + arrType[1] + "[" + cnt + "]");
1799+        } else {
1800+            // Get the namespace from mappings if available when arrayType is removed for .Net
1801+            if (itemsNamespace == null) {
1802+                itemsNamespace = (String) arrType[0];
1803+            }
1804         }
1805
1806         boolean skipped = false;
1807diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
1808new file mode 100644
1809index 0000000..fdbaa21
1810--- /dev/null
1811+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
1812@@ -0,0 +1,13 @@
1813+package org.ksoap2.serialization;
1814+
1815+import org.xmlpull.v1.XmlSerializer;
1816+
1817+import java.io.IOException;
1818+
1819+/**
1820+ * Created by robocik on 2015-09-25.
1821+ */
1822+public interface ValueWriter
1823+{
1824+    void write(XmlSerializer writer) throws IOException;
1825+}
1826diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
1827index 8e14ee7..9dd3837 100644
1828--- a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
1829+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
1830@@ -21,8 +21,10 @@
1831
1832 package org.ksoap2.transport;
1833
1834+import java.io.IOException;
1835+import java.io.InputStream;
1836+import java.io.OutputStream;
1837 import java.util.List;
1838-import java.io.*;
1839
1840 /**
1841  * Interface to allow the abstraction of the raw transport information
1842@@ -58,6 +60,13 @@ public interface ServiceConnection {
1843      */
1844     public List getResponseProperties() throws IOException;
1845
1846+    /**
1847+     * Returns the numerical HTTP status to the caller
1848+     * @return an integer status value
1849+     * @throws IOException
1850+     */
1851+    public int getResponseCode() throws IOException;
1852+
1853     /**
1854      * Set properties on the outgoing connection.
1855      *
1856@@ -88,6 +97,8 @@ public interface ServiceConnection {
1857      **/
1858     public void setFixedLengthStreamingMode(int contentLength);
1859
1860+    public void setChunkedStreamingMode();
1861+
1862     /**
1863      * Open and return the outputStream to the endpoint.
1864      *
1865diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
1866index b2f6587..2f3d523 100644
1867--- a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
1868+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
1869@@ -23,9 +23,13 @@
1870
1871 package org.ksoap2.transport;
1872
1873+import java.util.HashMap;
1874+import java.util.Iterator;
1875 import java.util.List;
1876 import java.io.*;
1877+import java.net.MalformedURLException;
1878 import java.net.Proxy;
1879+import java.net.URL;
1880
1881 import org.ksoap2.*;
1882 import org.kxml2.io.*;
1883@@ -40,9 +44,9 @@ import org.xmlpull.v1.*;
1884 abstract public class Transport {
1885
1886     /**
1887-     * Added to enable web service interactions on the emulator
1888-     * to be debugged with Fiddler2 (Windows) but provides utility
1889-     * for other proxy requirements.
1890+     * Added to enable web service interactions on the emulator to be debugged
1891+     * with Fiddler2 (Windows) but provides utility for other proxy
1892+     * requirements.
1893      */
1894     protected Proxy proxy;
1895     protected String url;
1896@@ -61,6 +65,12 @@ abstract public class Transport {
1897
1898     private int bufferLength = ServiceConnection.DEFAULT_BUFFER_SIZE;
1899
1900+    private HashMap prefixes = new HashMap();
1901+
1902+    public HashMap getPrefixes() {
1903+        return prefixes;
1904+    }
1905+
1906     public Transport() {
1907     }
1908
1909@@ -82,9 +92,12 @@ abstract public class Transport {
1910     /**
1911      * Construct the transport object
1912      *
1913-     * @param proxy Specifies the proxy server to use for
1914-     * accessing the web service or <code>null</code> if a direct connection is available
1915-     * @param url Specifies the web service url
1916+     * @param proxy
1917+     *            Specifies the proxy server to use for accessing the web
1918+     *            service or <code>null</code> if a direct connection is
1919+     *            available
1920+     * @param url
1921+     *            Specifies the web service url
1922      *
1923      */
1924     public Transport(Proxy proxy, String url) {
1925@@ -114,23 +127,30 @@ abstract public class Transport {
1926         xp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
1927         xp.setInput(is, null);
1928         envelope.parse(xp);
1929+        /*
1930+         * Fix memory leak when running on android in strict mode. Issue 133
1931+         */
1932+        is.close();
1933     }
1934
1935     /**
1936      * Serializes the request.
1937      */
1938-    protected byte[] createRequestData(SoapEnvelope envelope, String encoding) throws IOException {
1939-        System.out.println("createRequestData");
1940+    protected byte[] createRequestData(SoapEnvelope envelope, String encoding)
1941+            throws IOException {
1942         ByteArrayOutputStream bos = new ByteArrayOutputStream(bufferLength);
1943         byte result[] = null;
1944         bos.write(xmlVersionTag.getBytes());
1945-        System.out.println("bos.write");
1946         XmlSerializer xw = new KXmlSerializer();
1947-        System.out.println("new KXmlSerializer");
1948+
1949+        final Iterator keysIter = prefixes.keySet().iterator();
1950+
1951         xw.setOutput(bos, encoding);
1952-        System.out.println("xw.setOutput");
1953+        while (keysIter.hasNext()) {
1954+            String key = (String) keysIter.next();
1955+            xw.setPrefix(key, (String) prefixes.get(key));
1956+        }
1957         envelope.write(xw);
1958-        System.out.println("envelope.write");
1959         xw.flush();
1960         bos.write('\r');
1961         bos.write('\n');
1962@@ -138,14 +158,14 @@ abstract public class Transport {
1963         result = bos.toByteArray();
1964         xw = null;
1965         bos = null;
1966-        System.out.println("createRequestData end");
1967         return result;
1968     }
1969
1970     /**
1971      * Serializes the request.
1972      */
1973-    protected byte[] createRequestData(SoapEnvelope envelope) throws IOException {
1974+    protected byte[] createRequestData(SoapEnvelope envelope)
1975+            throws IOException {
1976         return createRequestData(envelope, null);
1977     }
1978
1979@@ -159,6 +179,12 @@ abstract public class Transport {
1980         this.url = url;
1981     }
1982
1983+    public String getUrl()
1984+    {
1985+        return url;
1986+    }
1987+
1988+
1989     /**
1990      * Sets the version tag for the outgoing soap call. Example <?xml
1991      * version=\"1.0\" encoding=\"UTF-8\"?>
1992@@ -177,57 +203,94 @@ abstract public class Transport {
1993     }
1994
1995     /**
1996-     * Perform a soap call with a given namespace and the given envelope providing
1997-     * any extra headers that the user requires such as cookies. Headers that are
1998-     * returned by the web service will be returned to the caller in the form of a
1999-     * <code>List</code> of <code>HeaderProperty</code> instances.
2000+     * Perform a soap call with a given namespace and the given envelope
2001+     * providing any extra headers that the user requires such as cookies.
2002+     * Headers that are returned by the web service will be returned to the
2003+     * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
2004+     * instances.
2005+     *
2006+     * @param soapAction
2007+     *            the namespace with which to perform the call in.
2008+     * @param envelope
2009+     *            the envelope the contains the information for the call.
2010+     * @param headers
2011+     *            <code>List</code> of <code>HeaderProperty</code> headers to
2012+     *            send with the SOAP request.
2013+     *
2014+     * @return Headers returned by the web service as a <code>List</code> of
2015+     *         <code>HeaderProperty</code> instances.
2016+     */
2017+    abstract public List call(String soapAction, SoapEnvelope envelope,
2018+            List headers) throws IOException, XmlPullParserException;
2019+
2020+    /**
2021+     * Perform a soap call with a given namespace and the given envelope
2022+     * providing any extra headers that the user requires such as cookies.
2023+     * Headers that are returned by the web service will be returned to the
2024+     * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
2025+     * instances.
2026      *
2027-     * @param targetNamespace
2028+     * @param soapAction
2029      *            the namespace with which to perform the call in.
2030      * @param envelope
2031      *            the envelope the contains the information for the call.
2032      * @param headers
2033-     *   <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
2034+     *            <code>List</code> of <code>HeaderProperty</code> headers to
2035+     *            send with the SOAP request.
2036+     * @param outputFile
2037+     *            a file to stream the response into rather than parsing it,
2038+     *            streaming happens when file is not null
2039      *
2040      * @return Headers returned by the web service as a <code>List</code> of
2041-     * <code>HeaderProperty</code> instances.
2042+     *         <code>HeaderProperty</code> instances.
2043      */
2044-    abstract public List call(String targetNamespace, SoapEnvelope envelope, List headers)
2045-            throws IOException, XmlPullParserException;
2046+    abstract public List call(String soapAction, SoapEnvelope envelope,
2047+            List headers, File outputFile) throws IOException,
2048+            XmlPullParserException;
2049
2050     /**
2051      * Perform a soap call with a given namespace and the given envelope.
2052      *
2053-     * @param targetNamespace
2054+     * @param soapAction
2055      *            the namespace with which to perform the call in.
2056      * @param envelope
2057      *            the envelope the contains the information for the call.
2058      */
2059-    public void call(String targetNamespace, SoapEnvelope envelope) throws IOException,
2060-            XmlPullParserException {
2061-        call(targetNamespace, envelope, null);
2062+    public void call(String soapAction, SoapEnvelope envelope)
2063+            throws IOException, XmlPullParserException {
2064+        call(soapAction, envelope, null);
2065     }
2066
2067     /**
2068      * Return the name of the host that is specified as the web service target
2069-     *
2070+     *
2071      * @return Host name
2072      */
2073-    abstract public String getHost();
2074+    public String getHost() throws MalformedURLException {
2075+
2076+        return new URL(url).getHost();
2077+    }
2078
2079     /**
2080-     * Return the port number of the host that is specified as the web service target
2081-     *
2082+     * Return the port number of the host that is specified as the web service
2083+     * target
2084+     *
2085      * @return Port number
2086      */
2087-    abstract public int getPort();
2088+    public int getPort() throws MalformedURLException {
2089+
2090+        return new URL(url).getPort();
2091+    }
2092
2093     /**
2094      * Return the path to the web service target
2095-     *
2096+     *
2097      * @return The URL's path
2098      */
2099-    abstract public String getPath();
2100+    public String getPath() throws MalformedURLException {
2101+
2102+        return new URL(url).getPath();
2103+    }
2104
2105     abstract public ServiceConnection getServiceConnection() throws IOException;
2106 }
2107diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
2108index 71c8caa..61a5e2c 100644
2109--- a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
2110+++ b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
2111@@ -27,8 +27,7 @@ import org.xmlpull.v1.*;
2112
2113 public class MarshalFloat implements Marshal {
2114
2115-    public Object readInstance(XmlPullParser parser, String namespace, String name,
2116-            PropertyInfo propertyInfo)
2117+    public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo propertyInfo)
2118             throws IOException, XmlPullParserException {
2119         String stringValue = parser.nextText();
2120         Object result;
2121diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
2122new file mode 100644
2123index 0000000..f7fb866
2124--- /dev/null
2125+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
2126@@ -0,0 +1,60 @@
2127+package org.ksoap2.transport;
2128+
2129+import java.io.IOException;
2130+import java.util.List;
2131+
2132+/**
2133+ * HttpResponseException is an IOException that is to be thrown when a Http response code is different from 200.
2134+ * It allows for easier retrieval of the Http response code from the connection.
2135+ *
2136+ * @author Rui Pereira <syshex@gmail.com>
2137+ */
2138+public class HttpResponseException extends IOException {
2139+
2140+    private int statusCode;
2141+    private List responseHeaders;
2142+
2143+    public HttpResponseException(int statusCode) {
2144+        super();
2145+        this.statusCode = statusCode;
2146+    }
2147+
2148+    public HttpResponseException(String detailMessage, int statusCode) {
2149+        super(detailMessage);
2150+        this.statusCode = statusCode;
2151+    }
2152+
2153+    public HttpResponseException(String detailMessage, int statusCode,List responseHeaders) {
2154+        super(detailMessage);
2155+        this.statusCode = statusCode;
2156+        this.responseHeaders=responseHeaders;
2157+    }
2158+
2159+    public HttpResponseException(String message, Throwable cause, int statusCode) {
2160+        super(message, cause);
2161+        this.statusCode = statusCode;
2162+    }
2163+
2164+    public HttpResponseException(Throwable cause, int statusCode) {
2165+        super(cause);
2166+        this.statusCode = statusCode;
2167+    }
2168+
2169+    /**
2170+     * Returns the unexpected Http response code
2171+     *
2172+     * @return response code
2173+     */
2174+    public int getStatusCode() {
2175+        return statusCode;
2176+    }
2177+
2178+    /**
2179+     * Returns all http headers from this response
2180+     *
2181+     * @return response code
2182+     */
2183+    public List getResponseHeaders() {
2184+        return responseHeaders;
2185+    }
2186+}
2187diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
2188index d92ac09..920ca60 100644
2189--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
2190+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
2191@@ -25,24 +25,21 @@
2192
2193 package org.ksoap2.transport;
2194
2195-import java.util.List;
2196-import java.util.zip.GZIPInputStream;
2197+import org.ksoap2.HeaderProperty;
2198+import org.ksoap2.SoapEnvelope;
2199+import org.ksoap2.serialization.*;
2200+import org.xmlpull.v1.XmlPullParserException;
2201+
2202 import java.io.*;
2203-import java.net.MalformedURLException;
2204 import java.net.Proxy;
2205-import java.net.URL;
2206-
2207-import org.ksoap2.*;
2208-import org.ksoap2.serialization.SoapSerializationEnvelope;
2209-import org.xmlpull.v1.*;
2210+import java.util.*;
2211+import java.util.zip.GZIPInputStream;
2212
2213 /**
2214  * A J2SE based HttpTransport layer.
2215  */
2216 public class HttpTransportSE extends Transport {
2217
2218-    private ServiceConnection serviceConnection;
2219-
2220     /**
2221      * Creates instance of HttpTransportSE with set url
2222      *
2223@@ -107,249 +104,254 @@ public class HttpTransportSE extends Transport {
2224      *            the desired soapAction
2225      * @param envelope
2226      *            the envelope containing the information for the soap call.
2227+     * @throws HttpResponseException
2228      * @throws IOException
2229      * @throws XmlPullParserException
2230      */
2231-    public void call(String soapAction, SoapEnvelope envelope) throws IOException,
2232-            XmlPullParserException {
2233-
2234+    public void call(String soapAction, SoapEnvelope envelope)
2235+            throws HttpResponseException, IOException, XmlPullParserException {
2236+
2237         call(soapAction, envelope, null);
2238     }
2239
2240+    public List call(String soapAction, SoapEnvelope envelope, List headers)
2241+            throws HttpResponseException, IOException, XmlPullParserException {
2242+        return call(soapAction, envelope, headers, null);
2243+    }
2244+
2245     /**
2246-     *
2247-     * set the desired soapAction header field
2248-     *
2249+     * Perform a soap call with a given namespace and the given envelope providing
2250+     * any extra headers that the user requires such as cookies. Headers that are
2251+     * returned by the web service will be returned to the caller in the form of a
2252+     * <code>List</code> of <code>HeaderProperty</code> instances.
2253+     *
2254      * @param soapAction
2255-     *            the desired soapAction
2256+     *            the namespace with which to perform the call in.
2257      * @param envelope
2258-     *            the envelope containing the information for the soap call.
2259+     *            the envelope the contains the information for the call.
2260      * @param headers
2261-     *              a list of HeaderProperties to be http header properties when establishing the connection
2262+     *   <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
2263+     * @param outputFile
2264+     *              a file to stream the response into rather than parsing it, streaming happens when file is not null
2265      *
2266-     * @return <code>CookieJar</code> with any cookies sent by the server
2267-     * @throws IOException
2268-     * @throws XmlPullParserException
2269+     * @return Headers returned by the web service as a <code>List</code> of
2270+     * <code>HeaderProperty</code> instances.
2271+     *
2272+     * @throws HttpResponseException
2273+     *              an IOException when Http response code is different from 200
2274      */
2275-    public List call(String soapAction, SoapEnvelope envelope, List headers)
2276-            throws IOException, XmlPullParserException {
2277+    public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
2278+        throws HttpResponseException, IOException, XmlPullParserException {
2279
2280         if (soapAction == null) {
2281             soapAction = "\"\"";
2282         }
2283
2284-        System.out.println("call action:" + soapAction);
2285         byte[] requestData = createRequestData(envelope, "UTF-8");
2286
2287-        if (requestData != null) {
2288-            requestDump = debug ? new String(requestData) : null;
2289-        }
2290-        else {
2291-            requestDump = null;
2292-        }
2293+        requestDump = debug ? new String(requestData) : null;
2294         responseDump = null;
2295-
2296-        System.out.println("requestDump:" + requestDump);
2297+        System.out.println("requestDump: " + requestDump);
2298         ServiceConnection connection = getServiceConnection();
2299-        System.out.println("connection:" + connection);
2300
2301         connection.setRequestProperty("User-Agent", USER_AGENT);
2302         // SOAPAction is not a valid header for VER12 so do not add
2303         // it
2304         // @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
2305-        System.out.println("envelope:" + envelope);
2306-        if (envelope != null) {
2307-            if (envelope.version != SoapSerializationEnvelope.VER12) {
2308-                connection.setRequestProperty("SOAPAction", soapAction);
2309-            }
2310+        if (envelope.version != SoapSerializationEnvelope.VER12) {
2311+            connection.setRequestProperty("SOAPAction", soapAction);
2312+        }
2313
2314-            if (envelope.version == SoapSerializationEnvelope.VER12) {
2315-                connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
2316-            } else {
2317-                connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
2318-            }
2319+        if (envelope.version == SoapSerializationEnvelope.VER12) {
2320+            connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
2321+        } else {
2322+            connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
2323+        }
2324
2325-            connection.setRequestProperty("Connection", "close");
2326-            connection.setRequestProperty("Accept-Encoding", "gzip");
2327-            connection.setRequestProperty("Content-Length", "" + requestData.length);
2328+        // this seems to cause issues so we are removing it
2329+        //connection.setRequestProperty("Connection", "close");
2330+        connection.setRequestProperty("Accept-Encoding", "gzip");
2331
2332-            //M: Retry for HTTP Authentication
2333-            //connection.setFixedLengthStreamingMode(requestData.length);
2334
2335-            // Pass the headers provided by the user along with the call
2336-            if (headers != null) {
2337-                for (int i = 0; i < headers.size(); i++) {
2338-                    HeaderProperty hp = (HeaderProperty) headers.get(i);
2339-                    connection.setRequestProperty(hp.getKey(), hp.getValue());
2340-                }
2341+        // Pass the headers provided by the user along with the call
2342+        if (headers != null) {
2343+            for (int i = 0; i < headers.size(); i++) {
2344+                HeaderProperty hp = (HeaderProperty) headers.get(i);
2345+                connection.setRequestProperty(hp.getKey(), hp.getValue());
2346             }
2347-
2348-            connection.setRequestMethod("POST");
2349-
2350-        }
2351-        else {
2352-            connection.setRequestProperty("Connection", "close");
2353-            connection.setRequestProperty("Accept-Encoding", "gzip");
2354-            connection.setRequestMethod("GET");
2355         }
2356
2357-        if (requestData != null) {
2358-            OutputStream os = connection.openOutputStream();
2359-
2360-            os.write(requestData, 0, requestData.length);
2361-            os.flush();
2362-            os.close();
2363-            requestData = null;
2364-        }
2365-        InputStream is;
2366+        connection.setRequestMethod("POST");
2367+        sendData(requestData, connection,envelope);
2368+        requestData = null;
2369+        InputStream is = null;
2370         List retHeaders = null;
2371+        byte[] buf = null; // To allow releasing the resource after used
2372+        int contentLength = 8192; // To determine the size of the response and adjust buffer size
2373         boolean gZippedContent = false;
2374-        boolean bcaCert = false;
2375+        boolean xmlContent = false;
2376+        int status = connection.getResponseCode();
2377
2378         try {
2379             retHeaders = connection.getResponseProperties();
2380-            System.out.println("[HttpTransportSE] retHeaders = " + retHeaders);
2381+
2382             for (int i = 0; i < retHeaders.size(); i++) {
2383-                HeaderProperty hp = (HeaderProperty) retHeaders.get(i);
2384+                HeaderProperty hp = (HeaderProperty)retHeaders.get(i);
2385                 // HTTP response code has null key
2386                 if (null == hp.getKey()) {
2387                     continue;
2388                 }
2389+
2390+                // If we know the size of the response, we should use the size to initiate vars
2391+                if (hp.getKey().equalsIgnoreCase("content-length") ) {
2392+                    if ( hp.getValue() != null ) {
2393+                        try {
2394+                            contentLength = Integer.parseInt( hp.getValue() );
2395+                        } catch ( NumberFormatException nfe ) {
2396+                            contentLength = 8192;
2397+                        }
2398+                    }
2399+                }
2400+
2401+
2402+                // Check the content-type header to see if we're getting back XML, in case of a
2403+                // SOAP fault on 500 codes
2404+                if (hp.getKey().equalsIgnoreCase("Content-Type")
2405+                        && hp.getValue().contains("xml")) {
2406+                    xmlContent = true;
2407+                }
2408+
2409+
2410                 // ignoring case since users found that all smaller case is used on some server
2411                 // and even if it is wrong according to spec, we rather have it work..
2412                 if (hp.getKey().equalsIgnoreCase("Content-Encoding")
2413-                        && hp.getValue().equalsIgnoreCase("gzip")) {
2414+                     && hp.getValue().equalsIgnoreCase("gzip")) {
2415                     gZippedContent = true;
2416                 }
2417             }
2418-            if (gZippedContent) {
2419-                is = getUnZippedInputStream(connection.openInputStream());
2420-            } else {
2421-                is = connection.openInputStream();
2422-            }
2423-        } catch (IOException e) {
2424-            if (gZippedContent) {
2425-                is = getUnZippedInputStream(connection.getErrorStream());
2426-            } else {
2427-                is = connection.getErrorStream();
2428-            }
2429
2430-            if (is == null) {
2431-                connection.disconnect();
2432-                throw (e);
2433+            //first check the response code....
2434+            if (status != 200 && status != 202) {
2435+                //202 is a correct status returned by WCF OneWay operation
2436+                throw new HttpResponseException("HTTP request failed, HTTP status: " + status, status,retHeaders);
2437             }
2438-        }
2439-
2440-        if (debug) {
2441-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
2442-            byte[] buf = new byte[8192];
2443
2444-            while (true) {
2445-                int rd = is.read(buf, 0, 8192);
2446-                if (rd == -1) {
2447-                    break;
2448+            if (contentLength > 0) {
2449+                if (gZippedContent) {
2450+                    is = getUnZippedInputStream(
2451+                            new BufferedInputStream(connection.openInputStream(),contentLength));
2452+                } else {
2453+                    is = new BufferedInputStream(connection.openInputStream(),contentLength);
2454+                }
2455+            }
2456+        } catch (IOException e) {
2457+            if (contentLength > 0) {
2458+                if(gZippedContent) {
2459+                    is = getUnZippedInputStream(
2460+                            new BufferedInputStream(connection.getErrorStream(),contentLength));
2461+                } else {
2462+                    is = new BufferedInputStream(connection.getErrorStream(),contentLength);
2463                 }
2464-                bos.write(buf, 0, rd);
2465             }
2466
2467-            bos.flush();
2468-            buf = bos.toByteArray();
2469+            if ( e instanceof HttpResponseException) {
2470+                if (!xmlContent) {
2471+                    if (debug && is != null) {
2472+                        //go ahead and read the error stream into the debug buffers/file if needed.
2473+                        readDebug(is, contentLength, outputFile);
2474+                    }
2475
2476-            responseDump = new String(buf);
2477+                    //we never want to drop through to attempting to parse the HTTP error stream as a SOAP response.
2478+                    connection.disconnect();
2479+                    throw e;
2480+                }
2481+            }
2482+        }
2483
2484-            System.out.println("responseDump:" + responseDump);
2485-            is.close();
2486-            is = new ByteArrayInputStream(buf);
2487+        if (debug) {
2488+            is = readDebug(is, contentLength, outputFile);
2489         }
2490
2491-        if (envelope != null) {
2492-            parseResponse(envelope, is);
2493+        if(is!=null)
2494+        {
2495+            parseResponse(envelope, is,retHeaders);
2496         }
2497
2498+        // release all resources
2499+        // input stream is will be released inside parseResponse
2500+        is = null;
2501+        buf = null;
2502+        //This fixes Issue 173 read my explanation here: https://code.google.com/p/ksoap2-android/issues/detail?id=173
2503+        connection.disconnect();
2504+        connection = null;
2505         return retHeaders;
2506     }
2507
2508-    private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
2509-        /* workaround for Android 2.3
2510-           (see http://stackoverflow.com/questions/5131016/)
2511-        */
2512-        try {
2513-            return (GZIPInputStream) inputStream;
2514-        } catch (ClassCastException e) {
2515-            return new GZIPInputStream(inputStream);
2516-        }
2517-    }
2518+    protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope)
2519+            throws IOException
2520+    {
2521+        connection.setRequestProperty("Content-Length", "" + requestData.length);
2522+        connection.setFixedLengthStreamingMode(requestData.length);
2523
2524-    public ServiceConnection getServiceConnection() throws IOException {
2525-        if (serviceConnection == null) {
2526-            System.out.println("new ServiceConnectionSE:" + proxy + " " + url + " " + timeout);
2527-            serviceConnection = new ServiceConnectionSE(proxy, url, timeout);
2528-        }
2529-        return serviceConnection;
2530+        OutputStream os = connection.openOutputStream();
2531+        os.write(requestData, 0, requestData.length);
2532+        os.flush();
2533+        os.close();
2534     }
2535
2536-    public String getHost() {
2537-
2538-        String retVal = null;
2539-
2540-        try {
2541-            retVal = new URL(url).getHost();
2542-        } catch (MalformedURLException e) {
2543-            e.printStackTrace();
2544-        }
2545-
2546-        return retVal;
2547+    protected void parseResponse(SoapEnvelope envelope, InputStream is,List returnedHeaders)
2548+            throws XmlPullParserException, IOException
2549+    {
2550+        parseResponse(envelope, is);
2551     }
2552
2553-    public int getPort() {
2554-
2555-        int retVal = -1;
2556
2557-        try {
2558-            retVal = new URL(url).getPort();
2559-        } catch (MalformedURLException e) {
2560-            e.printStackTrace();
2561+    private InputStream readDebug(InputStream is, int contentLength, File outputFile) throws IOException {
2562+        OutputStream bos;
2563+        if (outputFile != null) {
2564+            bos = new FileOutputStream(outputFile);
2565+        } else {
2566+            // If known use the size if not use default value
2567+            bos = new ByteArrayOutputStream( (contentLength > 0 ) ? contentLength : 256*1024);
2568         }
2569
2570-        return retVal;
2571-    }
2572-
2573-    public String getPath() {
2574-
2575-        String retVal = null;
2576+        byte[] buf = new byte[256];
2577
2578-        try {
2579-            retVal = new URL(url).getPath();
2580-        } catch (MalformedURLException e) {
2581-            e.printStackTrace();
2582+        while (true) {
2583+            int rd = is.read(buf, 0, 256);
2584+            if (rd == -1) {
2585+                break;
2586+            }
2587+            bos.write(buf, 0, rd);
2588         }
2589
2590-        return retVal;
2591-    }
2592-
2593-    public String getQuery() {
2594-
2595-        String retVal = null;
2596-
2597-        try {
2598-            retVal = new URL(url).getQuery();
2599-        } catch (MalformedURLException e) {
2600-            e.printStackTrace();
2601+        bos.flush();
2602+        if (bos instanceof ByteArrayOutputStream) {
2603+            buf = ((ByteArrayOutputStream) bos).toByteArray();
2604+        }
2605+        bos = null;
2606+        responseDump = new String(buf);
2607+        is.close();
2608+        System.out.println("responseDump: " + requestDump);
2609+        if (outputFile != null) {
2610+            return new FileInputStream(outputFile);
2611+        } else {
2612+            return new ByteArrayInputStream(buf);
2613         }
2614-
2615-        return retVal;
2616     }
2617
2618-    /**
2619-     * @hide
2620-     */
2621-    public byte[] getRequestData(SoapEnvelope envelope, String encoding) {
2622+    private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
2623+        /* workaround for Android 2.3
2624+           (see http://stackoverflow.com/questions/5131016/)
2625+        */
2626         try {
2627-            return createRequestData(envelope, encoding);
2628-        } catch (Exception e) {
2629-            e.printStackTrace();
2630+            return (GZIPInputStream) inputStream;
2631+        } catch (ClassCastException e) {
2632+            return new GZIPInputStream(inputStream);
2633         }
2634+    }
2635
2636-        return null;
2637+    public ServiceConnection getServiceConnection() throws IOException {
2638+        return new ServiceConnectionSE(proxy, url, timeout);
2639     }
2640 }
2641diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
2642index 9ad9ba9..376c7d8 100644
2643--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
2644+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
2645@@ -1,21 +1,16 @@
2646
2647 package org.ksoap2.transport;
2648
2649+import org.ksoap2.HeaderProperty;
2650+
2651+import javax.net.ssl.HttpsURLConnection;
2652+import javax.net.ssl.SSLSocketFactory;
2653 import java.io.IOException;
2654 import java.io.InputStream;
2655 import java.io.OutputStream;
2656+import java.net.Proxy;
2657 import java.net.URL;
2658-import java.util.Iterator;
2659-import java.util.LinkedList;
2660-import java.util.Map;
2661-import java.util.List;
2662-import java.util.Set;
2663-
2664-import javax.net.ssl.HostnameVerifier;
2665-import javax.net.ssl.SSLSocketFactory;
2666-import javax.net.ssl.HttpsURLConnection;
2667-//import com.android.okhttp.internal.http.HttpsURLConnectionImpl;
2668-import org.ksoap2.HeaderProperty;
2669+import java.util.*;
2670
2671 /**
2672  * HttpsServiceConnectionSE is a service connection that uses a https url connection and requires explicit setting of
2673@@ -49,10 +44,29 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
2674      * @param timeout the timeout for the connection in milliseconds
2675      * @throws IOException
2676      */
2677-    public HttpsServiceConnectionSE(String host, int port, String file,
2678-            int timeout) throws IOException {
2679-        connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file)
2680-                .openConnection();
2681+    public HttpsServiceConnectionSE(String host, int port, String file, int timeout) throws IOException {
2682+        this(null, host, port, file, timeout);
2683+    }
2684+
2685+    /**
2686+     * Create the transport with the supplied parameters.
2687+     * @param proxy proxy server to use
2688+     * @param host the name of the host e.g. webservices.somewhere.com
2689+     * @param port the http port to connect on
2690+     * @param file the path to the file on the webserver that represents the
2691+     * webservice e.g. /api/services/myservice.jsp
2692+     * @param timeout the timeout for the connection in milliseconds
2693+     * @throws IOException
2694+     */
2695+    public HttpsServiceConnectionSE(Proxy proxy, String host, int port, String file, int timeout) throws IOException {
2696+
2697+        if (proxy == null) {
2698+            connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection();
2699+        } else {
2700+            connection =
2701+                    (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection(proxy);
2702+        }
2703+
2704         updateConnectionParameters(timeout);
2705     }
2706
2707@@ -89,6 +103,10 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
2708         return retList;
2709     }
2710
2711+    public int getResponseCode() throws IOException {
2712+        return connection.getResponseCode();
2713+    }
2714+
2715     public void setRequestProperty(String key, String value) {
2716         connection.setRequestProperty(key, value);
2717     }
2718@@ -101,6 +119,11 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
2719         connection.setFixedLengthStreamingMode(contentLength);
2720     }
2721
2722+    public void setChunkedStreamingMode() {
2723+        connection.setChunkedStreamingMode(0);
2724+    }
2725+
2726+
2727     public OutputStream openOutputStream() throws IOException {
2728         return connection.getOutputStream();
2729     }
2730@@ -128,9 +151,4 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
2731     public void setSSLSocketFactory(SSLSocketFactory sf) {
2732         connection.setSSLSocketFactory(sf);
2733     }
2734-
2735-    public void setHostnameVerifier(HostnameVerifier v) {
2736-        connection.setHostnameVerifier(v);
2737-    }
2738-
2739 }
2740diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
2741index d220ac9..a7d7023 100644
2742--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
2743+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
2744@@ -1,8 +1,8 @@
2745-
2746 package org.ksoap2.transport;
2747
2748 import java.io.IOException;
2749 import java.net.MalformedURLException;
2750+import java.net.Proxy;
2751 import java.net.URL;
2752
2753 /**
2754@@ -14,17 +14,31 @@ import java.net.URL;
2755 public class HttpsTransportSE extends HttpTransportSE {
2756
2757     static final String PROTOCOL = "https";
2758+    private static final String PROTOCOL_FULL = PROTOCOL + "://";
2759+
2760+    //connection instance, used for setting the SSLSocketFactory
2761+    private HttpsServiceConnectionSE connection;
2762
2763-    private ServiceConnection serviceConnection = null;
2764-    private final String host;
2765-    private final int port;
2766-    private final String file;
2767-    private final int timeout;
2768+    protected final String host;
2769+    protected final int port;
2770+    protected final String file;
2771
2772-    public HttpsTransportSE(String host, int port, String file, int timeout) {
2773-        super(HttpsTransportSE.PROTOCOL + "://" + host + ":" + port + file);
2774-        System.out.println("Establistion connection to: " + HttpsTransportSE.PROTOCOL + "://"
2775-                + host + ":" + port + file);
2776+    public HttpsTransportSE (String host, int port, String file, int timeout) {
2777+        super(HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file, timeout);
2778+        this.host = host;
2779+        this.port = port;
2780+        this.file = file;
2781+    }
2782+
2783+    /**
2784+     * Creates instance of HttpTransportSE with set url and defines a
2785+     * proxy server to use to access it
2786+     *
2787+     * @param proxy
2788+     * Proxy information or <code>null</code> for direct access
2789+     */
2790+    public HttpsTransportSE(Proxy proxy, String host, int port, String file, int timeout) {
2791+        super(proxy, HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file);
2792         this.host = host;
2793         this.port = port;
2794         this.file = file;
2795@@ -37,48 +51,11 @@ public class HttpsTransportSE extends HttpTransportSE {
2796      */
2797     public ServiceConnection getServiceConnection() throws IOException
2798     {
2799-        if (serviceConnection == null) {
2800-            serviceConnection = new HttpsServiceConnectionSE(host, port, file, timeout);
2801-        }
2802-        return serviceConnection;
2803-    }
2804-
2805-    public String getHost() {
2806-
2807-        String retVal = null;
2808-
2809-        try {
2810-            retVal = new URL(url).getHost();
2811-        } catch (MalformedURLException e) {
2812-            e.printStackTrace();
2813+        if(connection != null) {
2814+            return connection;
2815+        } else {
2816+            connection = new HttpsServiceConnectionSE(proxy, host, port, file, timeout);
2817+            return connection;
2818         }
2819-
2820-        return retVal;
2821-    }
2822-
2823-    public int getPort() {
2824-
2825-        int retVal = -1;
2826-
2827-        try {
2828-            retVal = new URL(url).getPort();
2829-        } catch (MalformedURLException e) {
2830-            e.printStackTrace();
2831-        }
2832-
2833-        return retVal;
2834-    }
2835-
2836-    public String getPath() {
2837-
2838-        String retVal = null;
2839-
2840-        try {
2841-            retVal = new URL(url).getPath();
2842-        } catch (MalformedURLException e) {
2843-            e.printStackTrace();
2844-        }
2845-
2846-        return retVal;
2847     }
2848 }
2849diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
2850index 287fed1..65ba582 100644
2851--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
2852+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
2853@@ -24,18 +24,8 @@ import java.io.IOException;
2854  */
2855 public class KeepAliveHttpsTransportSE extends HttpsTransportSE
2856 {
2857-    private final String host;
2858-    private final int port;
2859-    private final String file;
2860-    private final int timeout;
2861-    private ServiceConnection serviceConnection;
2862-
2863-    public KeepAliveHttpsTransportSE(String host, int port, String file, int timeout) {
2864+    public KeepAliveHttpsTransportSE (String host, int port, String file, int timeout) {
2865         super(host, port, file, timeout);
2866-        this.host = host;
2867-        this.port = port;
2868-        this.file = file;
2869-        this.timeout = timeout;
2870     }
2871
2872     /**
2873@@ -47,11 +37,9 @@ public class KeepAliveHttpsTransportSE extends HttpsTransportSE
2874     //@Override
2875     public ServiceConnection getServiceConnection() throws IOException
2876     {
2877-        if (serviceConnection == null) {
2878-            serviceConnection = new HttpsServiceConnectionSEIgnoringConnectionClose(host, port,
2879-                    file, timeout);
2880-            serviceConnection.setRequestProperty("Connection", "keep-alive");
2881-        }
2882+        ServiceConnection serviceConnection =
2883+                new HttpsServiceConnectionSEIgnoringConnectionClose(host, port, file, timeout);
2884+        serviceConnection.setRequestProperty("Connection", "keep-alive");
2885         return serviceConnection;
2886     }
2887
2888diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
2889index 029ee9a..bfdfe11 100644
2890--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
2891+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
2892@@ -21,16 +21,16 @@
2893
2894 package org.ksoap2.transport;
2895
2896-import java.io.*;
2897-import java.net.*;
2898-import java.util.Iterator;
2899-import java.util.LinkedList;
2900-import java.util.List;
2901-import java.util.Map;
2902-import java.util.Set;
2903-
2904 import org.ksoap2.HeaderProperty;
2905
2906+import java.io.IOException;
2907+import java.io.InputStream;
2908+import java.io.OutputStream;
2909+import java.net.HttpURLConnection;
2910+import java.net.Proxy;
2911+import java.net.URL;
2912+import java.util.*;
2913+
2914 /**
2915  * Connection for J2SE environments.
2916  */
2917@@ -80,23 +80,29 @@ public class ServiceConnectionSE implements ServiceConnection {
2918         connection.disconnect();
2919     }
2920
2921-    public List getResponseProperties() {
2922-        Map properties = connection.getHeaderFields();
2923-        Set keys = properties.keySet();
2924+    public List getResponseProperties() throws IOException {
2925         List retList = new LinkedList();
2926
2927-        for (Iterator i = keys.iterator(); i.hasNext();) {
2928-            String key = (String) i.next();
2929-            List values = (List) properties.get(key);
2930-
2931-            for (int j = 0; j < values.size(); j++) {
2932-                retList.add(new HeaderProperty(key, (String) values.get(j)));
2933+        Map properties = connection.getHeaderFields();
2934+        if(properties != null) {
2935+            Set keys = properties.keySet();
2936+            for (Iterator i = keys.iterator(); i.hasNext();) {
2937+                String key = (String) i.next();
2938+                List values = (List) properties.get(key);
2939+
2940+                for (int j = 0; j < values.size(); j++) {
2941+                    retList.add(new HeaderProperty(key, (String) values.get(j)));
2942+                }
2943             }
2944         }
2945
2946         return retList;
2947     }
2948
2949+    public int getResponseCode() throws IOException {
2950+        return connection.getResponseCode();
2951+    }
2952+
2953     public void setRequestProperty(String string, String soapAction) {
2954         connection.setRequestProperty(string, soapAction);
2955     }
2956@@ -116,6 +122,10 @@ public class ServiceConnectionSE implements ServiceConnection {
2957         connection.setFixedLengthStreamingMode(contentLength);
2958     }
2959
2960+    public void setChunkedStreamingMode() {
2961+        connection.setChunkedStreamingMode(0);
2962+    }
2963+
2964     public OutputStream openOutputStream() throws IOException {
2965         return connection.getOutputStream();
2966     }
2967--
29682.17.0.441.gb46fe60e1d-goog
2969
2970