docs: def _serialize(self, target_obj, data_type=None, **kwargs):
          """Serialize data into a string according to type.
  
          :param target_obj: The data to be serialized.
          :param str data_type: The type to be serialized from.
          :rtype: str, dict
          :raises: SerializationError if serialization fails.
          """
          key_transformer = kwargs.get("key_transformer", self.key_transformer)
          keep_readonly = kwargs.get("keep_readonly", False)
          if target_obj is None:
              return None
  
          attr_name = None
          class_name = target_obj.__class__.__name__
  
          if data_type:
              return self.serialize_data(
                  target_obj, data_type, **kwargs)
  
          if not hasattr(target_obj, "_attribute_map"):
              data_type = type(target_obj).__name__
              if data_type in self.basic_types.values():
                  return self.serialize_data(
                      target_obj, data_type, **kwargs)
  
          # Force "is_xml" kwargs if we detect a XML model
          try:
              is_xml_model_serialization = kwargs["is_xml"]
          except KeyError:
              is_xml_model_serialization = kwargs.setdefault("is_xml", target_obj.is_xml_model())
  
          serialized = self._create_serialized_base(target_obj)  # Could be JSON or XML
          try:
              attributes = target_obj._attribute_map
              for attr, attr_desc in attributes.items():
                  attr_name = attr
                  if not keep_readonly and target_obj._validation.get(attr_name, {}).get('readonly', False):
                      continue
  
                  if attr_name == "additional_properties" and attr_desc["key"] == '':
                      if target_obj.additional_properties is not None:
                          serialized.update(target_obj.additional_properties)
                      continue
                  try:
                      ### Extract sub-data to serialize from model ###
                      orig_attr = getattr(target_obj, attr)
                      if target_obj.is_xml_model():
                          pass # Don't provide "transformer" for XML for now. Keep "orig_attr"
                      else: # JSON
                          keys, orig_attr = key_transformer(attr, attr_desc.copy(), orig_attr)
                          keys = keys if isinstance(keys, list) else [keys]
  
                      ### Serialize this data ###
                      kwargs["serialization_ctxt"] = attr_desc
                      new_attr = self.serialize_data(orig_attr, attr_desc['type'], **kwargs)
  
                      ### Incorporate this data in the right place ###
                      if is_xml_model_serialization:
                          xml_desc = attr_desc['xml']
                          xml_name = xml_desc['name']
                          if "attr" in xml_desc and xml_desc["attr"]:
                              serialized.set(xml_name, new_attr)
                              continue
                          if isinstance(new_attr, list):
                              serialized.extend(new_attr)
                          elif isinstance(new_attr, ET.Element):
                              # If the down XML has no XML/Name, we MUST replace the tag with the local tag. But keeping the namespaces.
                              if 'name' not in getattr(orig_attr, '_xml_map', {}):
                                  splitted_tag = new_attr.tag.split("}")
                                  if len(splitted_tag) == 2: # Namespace
                                      new_attr.tag = "}".join([splitted_tag[0], xml_name])
                                  else:
                                      new_attr.tag = xml_name
                              serialized.append(new_attr)
                          else:  # That's a basic type
                              # Integrate namespace if necessary
                              local_node = _create_xml_node(
                                  xml_name,
                                  xml_desc.get('prefix', None),
                                  xml_desc.get('ns', None)
                              )
                              local_node.text = str(new_attr)
                              serialized.append(local_node)
                      else: # JSON
                          for k in reversed(keys):
                              unflattened = {k: new_attr}
                              new_attr = unflattened
  
                          _new_attr = new_attr
                          _serialized = serialized
                          for k in keys:
                              if k not in _serialized:
                                  _serialized.update(_new_attr)
                              _new_attr = _new_attr[k]
                              _serialized = _serialized[k]
                  except ValueError:
                      continue
  
          except (AttributeError, KeyError, TypeError) as err:
              msg = "Attribute {} in object {} cannot be serialized.\n{}".format(
                  attr_name, class_name, str(target_obj))
              raise_with_traceback(SerializationError, msg, err)
          else:
              return serialized