2 Replies Latest reply on Mar 6, 2014 5:04 PM by tsuji

    parsing xml for complex type using sax

    toatul

      I have an xsd of below type:

       

      <xs:complexType name="itemInfo">

          <xs:sequence>

            <xs:element name="displayLineNumber" type="xs:string" minOccurs="0"/>

            <xs:element name="lineNumber" type="xs:integer" minOccurs="0"/>

            <xs:element name="parentLineNumber" type="xs:integer" minOccurs="0"/>

         <xs:element name="service" type="serviceInfo" minOccurs="0" maxOccurs="unbounded"/>

         </xs:sequence>

      </xs:complexType>

       

      <xs:complexType name="serviceInfo">

          <xs:sequence>

            <xs:element name="displayLineNumber" type="xs:string" minOccurs="0"/>

            <xs:element name="lineNumber" type="xs:integer" minOccurs="0"/>

            <xs:element name="serviceName" type="xs:string" minOccurs="0"/>

            <xs:element name="serviceDescription" type="xs:string" minOccurs="0"/>

            <xs:element name="subscriptionBand" type="subscriptionBandInfo" minOccurs="0" maxOccurs="unbounded"/>

          </xs:sequence>

      </xs:complexType>

       

      <xs:complexType name="subscriptionBandInfo">

          <xs:sequence>

            <xs:element name="min" type="xs:long"/>

            <xs:element name="max" type="xs:long"/>

            <xs:element name="duration" type="xs:string" minOccurs="0"/>

            <xs:element name="price" type="xs:decimal"/>

          </xs:sequence>

        </xs:complexType>

       

       

      I have written a handler and able to handle simple type but how I can handle serviceInfo and subscriptionBandInfo as itemInfo is my root element.

       

      My handler class is:

      public class ProductHandler

        extends DefaultHandler

      {

        //List to hold ProductInfo object

        private List<ProductInfo> productList = null;

        private ProductInfo product = null;

       

        public List<ProductInfo> getProductList()

        {

          return productList;

        }

        boolean bDisplayLineNumber = false;

        boolean bLineNumber = false;

        boolean bParentLineNumber = false;

       

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
          throws SAXException
        {
          if (qName.equalsIgnoreCase("item"))
          { //create a new ProductInfo and put it in Map

            //initialize ProductInfo object and set id attribute
            product = new ProductInfo();

            //initialize list
            if (productList == null)
              productList = new ArrayList<ProductInfo>();
          }
          else if (qName.equalsIgnoreCase("name"))
          {
            //set boolean values for fields, will be used in setting ProductInfo variables
            bName = true;
          }

          else if (qName.equalsIgnoreCase("displayLineNumber"))

          {

            bDisplayLineNumber = true;

          }
          else if (qName.equalsIgnoreCase("lineNumber"))
          {
            bLineNumber = true;
          }
          else if (qName.equalsIgnoreCase("parentLineNumber"))
          {
            bParentNumber = true;
          }

      }

       

        @Override

        public void endElement(String uri, String localName, String qName)

          throws SAXException

        {

          if (qName.equalsIgnoreCase("item"))

          {

            //add ProductInfo object to list

            productList.add(product);

          }

        }

       

        @Override
        public void characters(char ch[], int start, int length)
          throws SAXException
        {

         if (bDisplayLineNumber)

          {

            product.setDisplayLineNumber(Integer.parseInt(new String(ch, start, length)));

            bDisplayLineNumber = false;

          }
         else if (bLineNumber)
          {

            product.setLineNumber(Integer.parseInt(new String(ch, start, length)));
            bLineNumber = false;
          }
          else if (bParentNumber)
          {
            product.setParentNumber(Integer.parseInt(new String(ch, start, length)));
            bParentNumber = false;
          }

      }

       

        @Override

        public void endElement(String uri, String localName, String qName)

          throws SAXException

        {

          if (qName.equalsIgnoreCase("item"))

          {

            //add ProductInfo object to list

            productList.add(product);

          }

        }

      }

       

      My ProductInfo class is:

       

      import com.vpc.schema.ServiceInfo;

      import java.util.ArrayList;
      import java.util.List;

      public class ProductInfo
      {
        private String category, family, subGroup, size, productType, availability;
        private String displayLineNumber;
        private int lineNumber;
        private int parentNumber;

      private List<ServiceInfo> serviceInfo;

        public int getLineNumber()
        {
          return lineNumber;
        }

        public int getParentNumber()
        {
          return parentNumber;
        }

        public List<ServiceInfo> getServiceInfo()

        {

          if (serviceInfo == null)

          {

            serviceInfo = new ArrayList<ServiceInfo>();

          }

          return serviceInfo;

        }

        public void setServiceInfo(List<ServiceInfo> serviceInfo)

        {

          this.serviceInfo = serviceInfo;

        }

      }

       

       

      I am able to do parsing for my simple type but when a complex type comes I am not able to do it. So please suggest how I can add complex type

        • 1. Re: parsing xml for complex type using sax
          dvohra21

          XML Schemas are used to validate an XML document, not to be parsed.

          • 2. Re: parsing xml for complex type using sax
            tsuji

            I suppose the posting of xsd is to show the structure of the xml...

            In any case, I can suggest a couple of things to do for the purpose.

            [1] If you still follow the same line of reasoning using some boolean like bDisplayLineNumber etc to identify the position of the parser traversing the document, you can complete the logic adding bItem (which you did not need under simplified consideration) and bService and bSubscriptionBand to identify at the parent or the grandparent in the case of the "complexType" serviceInfo and even the great-grand-parent in the case of arriving to the complexType subscriptionBandInfo...

            [1.1] With those boolean value, you determine bDisplayLineNumber etc under item directly, and then as well say bDisplayLineNumber_Service under the service etc and then bMin_SubscriptionBand etc under subscriptionBand etc. You just expand the number of those variables to trigger the setting of those fields in the object product, service in the serviceList and subscriptionBand in the subscriptionBandList etc etc.

            [1.2] All the reset of those booleans should be done in the endElement() method rather than in characters() method. That is logically more satisfactory and has a bearing in case there is a mixed content type.

            [1.3] Then when arriving at startElement of service, you make sure you initiate the serviceList, same for subscriptionBand the subscriptionList...

            [1.4] Then when arriving at endElement of service, you use setServiceInfo() setter to pass the serviceList to product and the same when arriving at endElement of serviceBand, you use setSubscriptionBand() setter to pass the subscriptionBand to service.

            ... and then basically that's is it, somewhat laborious and repetitive but the logical layout is clear. (As a side-note, you shouldn't use equalsIgnoreCase(), why should you? xml is case sensitive.)

            [2] Actually, I would suggest a much neater approach, especially when you probe many more levels of complexType. It would be even appear cleaner when you have two levels of depth already...

            [2.1] You maintain a Stack (or an implementation class of Deque, but Stack is largely sufficient here) of element name to guide the parser identifying its whereabout. By such doing, you get ride of all those bXXX's.

            This is a rewrite of the content handler using this approach and I only write the code of capturing service. Adding serviceBand is a matter of repetition of how it is done on service. And it is already clear it appears a lot neater as far as I'm concerned.

            public class ProductHandler extends DefaultHandler {
            Stack<String> tagstack=new Stack<String>();

            private List<ProductInfo> productList = null;
            private ProductInfo product = null;

            private List<ServiceInfo> serviceList=null;
            private ServiceInfo service=null;

            public List<ProductInfo> getProductList() {
              return productList;
            }

            public List<ServiceInfo> getServiceList() {
              return serviceList;
            }

            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) {
             
              if (qName.equals("item")) {
               product = new ProductInfo();
               //initialize list
               if (productList == null) {
                productList = new ArrayList<ProductInfo>();
               }
              } else if (qName.equals("service") && tagstack.peek().equals("item")) {
               service=new ServiceInfo();
               if (serviceList==null) {
                serviceList=new ArrayList<ServiceInfo>();
               }
              }
              tagstack.push(qName);
            }

            @Override
            public void endElement(String uri, String localName, String qName) {
              if (tagstack.peek().equals("item")) {
               //add ProductInfo object to list
               productList.add(product);
              } else if (tagstack.peek().equals("service") && tagstack.search("item")==2) {
               serviceList.add(service);
               product.setServiceInfo(serviceList);
              }
              tagstack.pop();
            }

            @Override
            public void characters(char ch[], int start, int length) throws SAXException {
              String currentName=tagstack.peek();
              int itemPos=tagstack.search("item");
              int servicePos=tagstack.search("service");
             
              if (currentName.equals("name") && itemPos==2)  {
               product.setName(new String(ch, start, length));
              } else if (currentName.equals("displayLineNumber") && itemPos==2) {
               product.setDisplayLineNumber(Integer.parseInt(new String(ch, start, length)));
              } else if (currentName.equals("lineNumber") && itemPos==2) {
               product.setLineNumber(Integer.parseInt(new String(ch, start, length)));
              } else if (currentName.equals("parentLineNumber") && itemPos==2) {
               product.setParentLineNumber(Integer.parseInt(new String(ch, start, length)));
              } else if (currentName.equals("displayLineNumber") && servicePos==2 && itemPos==3) {
               service.setDisplayLineNumber(Integer.parseInt(new String(ch, start, length)));
              } else if (currentName.equals("lineNumber") && servicePos==2 && itemPos==3) {
               service.setLineNumber(Integer.parseInt(new String(ch, start, length)));
              } else if (currentName.equals("serviceName") && servicePos==2 && itemPos==3) {
               service.setServiceName(new String(ch, start, length));
              } else if (currentName.equals("serviceDescription") && servicePos==2 && itemPos==3) {
               service.setServiceDescription(new String(ch, start, length));
              }
            }

            }