zoukankan      html  css  js  c++  java
  • ASP.NET Web API中的JSON和XML序列化

    ASP.NET Web API中的JSON和XML序列化

    前言

    阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html

    本文描述ASP.NET Web API中的JSON和XML格式化器。

    在ASP.NET Web API中,媒体类型格式化器(Media-type Formatter)是一种能够做以下工作的对象:

    • 从HTTP消息体读取CLR(公共语言运行时)对象
    • 将CLR对象写入HTTP消息体

    Web API提供了用于JSON和XML的媒体类型格式化器。框架已默认将这些格式化器插入到消息处理管线之中。客户端在HTTP请求的Accept报头中可以请求JSON或XML。

    JSON媒体类型格式化器

     JSON格式化是由JsonMediaTypeFormatter类提供的。默认情况下,JsonMediaTypeFormatter使用Json.NET库执行序列化工作。Json.NET是一个第三方开源项目。

    如果喜欢,你可以将JsonMediaTypeFormatter配置成使用DataContractJsonSerializer来代替Json.NET。要想这么做,只需UseDataContractJsonSerializer将属性设置为true即可:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.UseDataContractJsonSerializer = true;

    JSON序列化

    本小节描述,在使用默认的Json.NET序列化器时,JSON格式化器的一些特定行为。这并不意味着要包含Json.NET库的整个文档。更多信息参阅Json.NET Documentation。

    什么会被序列化?

    默认情况下,所有public属性和字段都会被包含在序列化的JSON中。为了忽略一个属性或字段,需要用JsonIgnore注解属性修饰它。

    复制代码
    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    
        [JsonIgnore]
        public int ProductCode { get; set; } // omitted
    }
    复制代码

    如果你更喜欢“opt-in(选入)”方法,可以用DataContract注解属性来修饰类。如果有注解属性,则成员均被忽略,除非有DataMemberDataMember也可以序列化private成员。

    复制代码
    [DataContract]
    public class Product
    {
        [DataMember]
        public string Name { get; set; }
    
        [DataMember]
        public decimal Price { get; set; }
        public int ProductCode { get; set; }  // omitted by default
    }
    复制代码

    只读属性

    只读属性默认是序列化的。

    Dates(日期)

    默认情况下,Json.NET会将日期写成ISO 8601格式。UTC(Coordinated Universal Time — 世界标准时间)格式的日期书写时带有后缀“Z”。本地时间格式的日期包括了一个时区偏移量。例如:

    2012-07-27T18:51:45.53403Z         // UTC(标准时间)
    2012-07-27T11:51:45.53403-07:00    // Local(本地时间)

    默认情况下,Json.NET保留时区。通过设置DateTimeZoneHandling属性,可以重写这一行为:

    // Convert all dates to UTC
    // 将所有日期转换成UTC格式
    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.DateTimeZoneHandling =
         Newtonsoft.Json.DateTimeZoneHandling.Utc;

    如果你喜欢使用微软的JSON日期格式("/Date(ticks)/ ")而不是ISO 8601,可以在SerializerSettings上设置DateFormatHandling属性:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.DateFormatHandling =
        Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat;

    Indenting(缩进)

    为了书写有缩进的JSON,可以将Formatting设置为Formatting.Indented

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.Formatting = 
        Newtonsoft.Json.Formatting.Indented; 

    Camel Casing(驼峰式大小写转换)

    为了在不修改数据模型的情况下,用驼峰式大小写转换JSON的属性名,可以设置序列化器上的CamelCasePropertyNamesContractResolver

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.ContractResolver = 
        new CamelCasePropertyNamesContractResolver();

    匿名类型与弱类型对象

    动作方法或以返回一个匿名对象,并将其序列化成JSON。例如:

    复制代码
    public object Get()
    {
        return new { 
            Name = "Alice", 
            Age = 23, 
            Pets = new List<string> { "Fido", "Polly", "Spot" } 
        };
    }
    复制代码

    响应消息体将含有以下JSON:

    {"Name":"Alice","Age":23,"Pets":["Fido","Polly","Spot"]}

    如果Web API从客户端接收了松散结构的JSON,你可以将该请求体解序列化成Newtonsoft.Json.Linq.JObject类型。

    public void Post(JObject person)
    {
        string name = person["Name"].ToString();
        int age = person["Age"].ToObject<int>();
    }

    然而,通常更好的是使用强类型数据对象。那么,便不需要自行对数据进行解析,并且能得到模型验证的好处。

    XML序列化器不支持匿名类型或JObject实例。如果将这些特性用于JSON数据,应该去掉管线中的XML格式化器,如本文稍后描述的那样。

    XML媒体类型格式化器

      XML格式化是由XmlMediaTypeFormatter类提供的。默认情况下,XmlMediaTypeFormatter使用DataContractSerializer类来执行序列化。如果喜欢,你可以将XmlMediaTypeFormatter配置成使用XmlSerializer而不是DataContractSerializer。要想这么做,可将UseXmlSerializer属性设置为true 

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    xml.UseXmlSerializer = true;

    XmlSerializer类支持的类型集要比DataContractSerializer更窄一些,但对结果XML有更多的控制。如果需要与已有的XML方案匹配,可考虑使用XmlSerializer

    XML Serialization——XML序列化

     本小节描述使用默认DataContractSerializer的时,XML格式化器的一些特殊行为。默认情况下,DataContractSerializer行为如下:

    •   序列化所有public读/写属性和字段。为了忽略一个属性或字段,请用IgnoreDataMember注解属性修饰它。
    •   private和protected成员不作序列。
    •   只读属性不作序列化
    •   类名和成员名按类声明中的确切呈现写入XML
    •   使用XML的默认命名空间

    如果需要在序列化上的更多控制,可以用DataContract注解属性修饰类。当这个注解属性出现时,该类按以策略序列化:

    •   “Opt in(选入)”方法:属性与字段默认不被序列化。为了序列化一个属性或字段,请用DataMember注解属性修饰它。
    •   要序列化private或protected成员,请用DataMember注解属性修饰它。
    •   只读属性不被序列化。
    •   要改变类名在XML中的呈现,请在DataContract注解属性中设置Name参数。
    •   要改变成员名在XML中的呈现,请设置DataMember注解属性中的Nmae参数。
    •   要改变XML命名空间,请设置DataContract类中的Namespace参数。

    Read-Only Properties——只读属性

    只读属性是不被序列化的。如果只读属性有一个支撑private字段,可以用DataMember注解属性对这个private字段进行标记。这种办法需要在类上使用DataContract注解属性。

    复制代码
    [DataContract]
    public class Product
    {
        [DataMember]
        private int pcode;  // serialized(序列化的)
    
        // Not serialized (read-only)
        // 不作序列化(只读)
        public int ProductCode { get { return pcode; } }
    }
    复制代码

    Dates——日期

    日期被写成ISO 8601格式。例如,“2012-05-23T20:21:37.9116538Z”。

    Indenting——缩进

    要书写缩进的XML,请将Indent属性设置为true

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    xml.Indent = true; 

    设置每一类型(Per-Type)的XML序列化器

    你可以为不同的CLR类型设置不同的XML序列化器。例如,你可能有一个特殊的数据对象,它出于向后兼容而需要XmlSerializer。你可以为此对象使用XmlSerializer,而对其它类型继续使用DataContractSerializer

    为了设置用于特殊类型的XML序列化器,要调用SetSerializer

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    // Use XmlSerializer for instances of type "Product":
    // 对“Product”类型的实例使用XmlSerializer:
    xml.SetSerializer<Product>(new XmlSerializer(typeof(Product)));

    你可以指定一个XmlSerializer,或任何派生于XmlObjectSerializer的对象。

    Removing the JSON or XML Formatter——去除JSON或XML格式化器

     你可以从格式化器列表中删除JSON格式化器,或XML格式化器,只要你不想使用它们。这么做的主要原因是:

    •    将你的Web API响应限制到特定的媒体类型。例如,你可能决定只支持JSON响应,而删除XML格式化器。
    •    用一个自定义格式化器代替默认的格式化器。例如,你可能要用自己的自定义JSON格式化器实现来代替(默认的)JSON格式化器。

     以下代码演示了如何删除默认的格式化器。在Global.asax中定义的Application_Start方法中调用它。

    复制代码
    void ConfigureApi(HttpConfiguration config)
    {
        // Remove the JSON formatter
        // 删除JSON格式化器
        config.Formatters.Remove(config.Formatters.JsonFormatter);
    
        // or(或者)
    
        // Remove the XML formatter
        // 删除XML格式化器
        config.Formatters.Remove(config.Formatters.XmlFormatter);
    }
    复制代码

    Handling Circular Object References——处理循环对象引用

     在默认情况下,JSON和XML格式化器将所有对象都写成值。如果两个属性引用了同一个对象,或者,如果在一个集合同一个对象出现了两次,格式化器将对此对象做两次序列化。这是在对象图含有循环的情况下会出现的特有问题,因为,序列化器在检测到对象图中的循环时,会抛出异常。

    考虑以下对象模型和控制器。

    复制代码
    public class Employee
    {
        public string Name { get; set; }
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public string Name { get; set; }
        public Employee Manager { get; set; }
    }
    
    public class DepartmentsController : ApiController
    {
        public Department Get(int id)
        {
            Department sales = new Department() { Name = "Sales" };
            Employee alice = new Employee() { Name = "Alice", Department = sales };
            sales.Manager = alice;
            return sales;
        }
    }
    复制代码

    调用此动作会触发格式化器抛出异常,该异常将转换成发送给客户端的状态代码500(内部服务器错误)响应。

    为了保留JSON中的对象引用,对Global.asax文件的Application_Start方法添加以下代码:

    var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
    json.SerializerSettings.PreserveReferencesHandling = 
        Newtonsoft.Json.PreserveReferencesHandling.All;

    现在,此控制器动作将返回类似于如下形式的JSON:

    {"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}

    注意,序列化器对两个对象都添加了一个“$id”。而且,它检测到Employee.Department属性产生了一个循环,因此,它用一个对象引用{"$ref":"1"}代替这个值。

    对象引用是不标准的JSON。在使用此特性之前,要考虑你的客户端是否能够解析这种结果。简单地去除对象图中的循环,可能是更好的办法。例如,此例中Employee链接回Department并不是真正的需要。

    为了保留XML中的对象引用,可以使用两个选项。较简单的选项是对模型类添加[DataContract(IsReference=true)]。IsReference参数启用了对象引用。记住,DataContract构成了序列化的“选入(Opt-in)”,因此,你还需要对属性添加DataMember注解属性:

    复制代码
    [DataContract(IsReference=true)]
    public class Department
    {
        [DataMember]
        public string Name { get; set; }
    
        [DataMember]
        public Employee Manager { get; set; }
    }
    复制代码

    现在,该格式化器将产生类似于如下形式的XML:

    复制代码
    <Department xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1" 
                xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" 
                xmlns="http://schemas.datacontract.org/2004/07/Models">
        <Manager>
            <Department z:Ref="i1" />
            <Name>Alice</Name>
        </Manager>
        <Name>Sales</Name>
    </Department>
    复制代码

    如果想避免在模型类上使用注解属性,还有另一个选项:创建新的类型专用的DataContractSerializer实例,并在构造器中将preserveObjectReferences设置为true

    var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    var dcs = new DataContractSerializer(typeof(Department), null, int.MaxValue,
        false, /* preserveObjectReferences: */ true, null);
    xml.SetSerializer<Department>(dcs);

    Testing Object Serialization——测试对象序列化

     在设计Web API时,对如何序列化对象进行测试是有用的。不必创建控制器或调用控制器动作,便可做这种事。

    复制代码
    string Serialize<T>(MediaTypeFormatter formatter, T value)
    {
        // Create a dummy HTTP Content.
        // 创建一个HTTP内容的哑元
        Stream stream = new MemoryStream();
        var content = new StreamContent(stream);
    
        // Serialize the object.
        // 序列化对象
        formatter.WriteToStreamAsync(typeof(T), value, stream, content.Headers, null).Wait();
    
        // Read the serialized string.
        // 读取序列化的字符串
        stream.Position = 0;
        return content.ReadAsStringAsync().Result;
    }
    
    T Deserialize<T>(MediaTypeFormatter formatter, string str) where T : class
    {
        // Write the serialized string to a memory stream.
        // 将序列化的字符器写入内在流
        Stream stream = new MemoryStream();
        StreamWriter writer = new StreamWriter(stream);
        writer.Write(str);
        writer.Flush();
        stream.Position = 0;
    
        // Deserialize to an object of type T
        // 解序列化成类型为T的对象
        return formatter.ReadFromStreamAsync(typeof(T), stream, null, null).Result as T;
    }
    
    // Example of use
    // 使用示例(用例)
    void TestSerialization()
    {
        var value = new Person() { Name = "Alice", Age = 23 };
    
        var xml = new XmlMediaTypeFormatter();
        string str = Serialize(xml, value);
    
        var json = new JsonMediaTypeFormatter();
        str = Serialize(json, value);
    
        // Round trip
        // 反向操作(解序列化)
        Person person2 = Deserialize<Person>(json, str);
    }
    复制代码

    总结

     本课主要简单的了解一下JSON和XML的序列化和反序列的使用。

     本文的参考链接为 http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

     同时本文已更新至 Web API导航系列 http://www.cnblogs.com/aehyok/p/3446289.html

     
     

    跨浏览器开发工作小结

     

      本篇小结是在2011年时候总结的,当时做一个产品的跨浏览器兼容工作,由于产品开发的时间比较早,最开始只能在IE下面(IE 8、IE 9还有点点问题)使用,做跨浏览器兼容工作的时候,主要是适配IE 6--IE 9、Safari、FireFoxChrome,引入了jQuery框架进行改造后,大部分功能可以正常使用,现将总结分享一下。

    1.eval(idName)

      【问题描述】:IEsafariChrome浏览器下都可以使用eval(idName)getElementById(idName)来取得ididNameHTML对象;firefox下只能使用getElementById(idName)来取得ididNameHTML对象.

      【兼容办法】:统一用getElementById("idName")来取得ididNameHTML对象。

    2.ActiveXObject 

      【问题描述】:IE下支持用var obj = new ActiveXObject() 的方式创建对象,但其它浏览器都会提示ActiveXObject对象未定义。 

      【兼容办法】:

      (1)在使用new ActiveXObject()之前先判断浏览器是否支持ActiveXObject对象,以创建AJAX对象为例:

    复制代码
    1 if(window.ActiveXObject)
    2 {
    3     this.req=new ActiveXObject("Microsoft.XMLHTTP");
    4 }
    5 else if(window.XMLHttpRequest)
    6 {
    7     this.req=new XMLHttpRequest();
    8 }
    复制代码

      (2)使用jQuery封装的ajax方法来创建对象,以创建AJAX对象为例(推荐)

    复制代码
     1 var strResponse = "";
     2 jQuery.ajax({ url: sAspFile, data: "<root>" + sSend + "</root>", processData: false, async: false, type: "POST",
     3     error: function(XMLHttpRequest, textStatus, errorThrown) 
     4     {
     5             strResponse = textStatus;
     6     },
     7      success: function(data, textStatus)
     8      {
     9             strResponse = data;
    10     } 
    11 });    
    复制代码

     3.XML操作 

      【问题描述】:通常装载xml文档使用ActiveXObject对象,但除非IE外,其它浏览器都不支持此方法。XML文档操作,IE和其它浏览器也存在不同,通常取XML对象的XML文本的方法是xml.documentElement.xml,但xml属性只有IE支持,其它浏览器均不支持。查找节点是常用的方法有selectNodesselectSingleNode,这两个方法也只有IE支持,其它浏览器需要自己扩展。

      【兼容办法】

      (1)装载XML文档:用$.ajax(),参考jquery帮助文档

      (2)xml对象转字符串,如:

    复制代码
     1 var stringtoxml = function(str) { //字符串转xml对象
     2     var s = "<?xml version='1.0' encoding='utf-8' ?>" + str;
     3     var objxml = null;
     4     if (window.ActiveXObject) {
     5         objxml = new ActiveXObject("Microsoft.XMLDOM");
     6         objxml.async = false;
     7         objxml.loadXML(s);
     8     }
     9     else {
    10         objxml = (new DOMParser()).parseFromString(s, "text/xml");
    11     }
    12     return objxml;
    13 }
    14 
    15 var xmltostring = function(dom) {  //xml对象转字符串
    16     if (dom instanceof jQuery) {
    17         dom = dom[0];
    18     }
    19     var str = null;
    20     if (window.ActiveXObject) {
    21         str = dom.xml;
    22     }
    23     else {
    24         str = (new XMLSerializer()).serializeToString(dom);
    25     }
    26     return str;
    27 }
    28 
    29 var oXMLO = stringtoxml("<root>"+ xml +"</root>");
    30 var root = oXMLO.documentElement;
    31 var strXml = xmltostring(root).replace("<root>","");
    复制代码

      (3)字符串转xml对象,如:

    1 var oXML = stringtoxml("<root>" + document.getElementById("hidTaskXml").value + "</root>");

      (4)查找结点:可以用JQUERY同的find方法来查找结点,如:

    1 var item = $(oXML).find("record");

      或者用原型扩展方法为XML对象添加selectNodesselectSingleNode方法,扩展方法如下:

    复制代码
    if( document.implementation.hasFeature("XPath", "3.0") ) 
    {
        XMLDocument.prototype.selectNodes =function(cXPathString, xNode) 
        {
            if( !xNode ) 
            { 
                xNode = this; 
            } 
            var oNSResolver = this.createNSResolver(this.documentElement); 
    
            var aItems = this.evaluate(cXPathString, xNode, oNSResolver,
    
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null) ;
    
            var aResult = []; 
    
            for( var i = 0; i < aItems.snapshotLength; i++)
            {     
                aResult[i] = aItems.snapshotItem(i); 
            } 
        
            return aResult; 
         } 
    
        Element.prototype.selectNodes = function(cXPathString)
         {     
            if(this.ownerDocument.selectNodes)
            {          
                return this.ownerDocument.selectNodes(cXPathString, this);     
            } 
            else
            {     
                throw "For XML Elements Only";    
            }  
         }
    
        XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode) 
        {
            if( !xNode ) 
            {  
               xNode = this; 
            } 
            var xItems = this.selectNodes(cXPathString, xNode);   
            if( xItems.length > 0 )
            {     
                 return xItems[0];   
            } 
            else 
            {   
                return null;   
            } 
        } 
    
        Element.prototype.selectSingleNode = function(cXPathString) 
        {    
            if(this.ownerDocument.selectSingleNode) 
            {       
                return this.ownerDocument.selectSingleNode(cXPathString, this);   
            } 
            else
            {      
                throw "For XML Elements Only";
            }    
         }  
    }            
    复制代码

    4.window.execScript() 

      【问题描述】:只有IE浏览器支持execScript方法,其它的都不支持。但所有浏览器都支持window.eval()方法。

      【兼容办法】:window.eval()方法代替window.execScript()。如

    1 //window.execScript(“alert(123)”);
    2 
    3 window.eval(“alert(123)”);

    5.window.createPopup()

      【问题描述】:创建一个弹出窗口的方法,IE支持此方法,SafariFireFoxChrome都不支持,使用时会提示createPopup方法未定义。

      【兼容办法】:可用如下方法为window对象添加createPopup方法。 

    复制代码
    if (!window.createPopup) {   
        var __createPopup = function() {   
            var SetElementStyles = function( element, styleDict ) {   
                var style = element.style ;   
                for ( var styleName in styleDict )style[ styleName ] = styleDict[ styleName ] ;    
            }   
            var eDiv = document.createElement( 'div' );    
            SetElementStyles( eDiv, { 'position': 'absolute', 'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 'zIndex': 1000, 'display' : 'none', 'overflow' : 'hidden' } ) ;   
            eDiv.body = eDiv ;   
            var opened = false ;   
            var setOpened = function( b ) {   
                opened = b;    
            }   
    
            var getOpened = function() {   
                return opened ;    
            }   
    
            var getCoordinates = function( oElement ) {   
                var coordinates = {x:0,y:0} ;    
                while( oElement ) {   
                    coordinates.x += oElement.offsetLeft ;   
                    coordinates.y += oElement.offsetTop ;   
                    oElement = oElement.offsetParent ;   
                }   
                return coordinates ;   
            }   
            return {
                htmlTxt : '', 
          document : eDiv, 
          isOpen : getOpened(), 
          isShow : false, 
          hide : function() { 
           SetElementStyles( eDiv, { 'top': 0 + 'px', 'left': 0 + 'px', 'width': 0 + 'px', 'height': 0 + 'px', 'display' : 'none' } ) ; 
           eDiv.innerHTML = '' ; 
           this.isShow = false ; 
          }, 
          show : function( iX, iY, iWidth, iHeight, oElement ) { 
           if (!getOpened()) { 
            document.body.appendChild( eDiv ) ; setOpened( true ) ; 
           } ; 
           this.htmlTxt = eDiv.innerHTML ; 
           if (this.isShow) { 
               this.hide() ; 
           } ; 
           eDiv.innerHTML = this.htmlTxt ; 
           var coordinates = getCoordinates ( oElement ) ; 
           eDiv.style.top = ( iX + coordinates.x ) + 'px' ; 
           eDiv.style.left = ( iY + coordinates.y ) + 'px' ; 
           eDiv.style.width = iWidth + 'px' ; 
           eDiv.style.height = iHeight + 'px' ; 
           eDiv.style.display = 'block' ; 
           this.isShow = true ; 
          } 
      }   
        }   
        window.createPopup = function() {   
            return __createPopup();    
        }   
    }
    复制代码

    6.getYear()方法

      【问题描述】:如下代码:

    1 var year= new Date().getYear();
    2 
    3 document.write(year);

      在IE中得到的日期是"2011",在Firefox中看到的日期是"111",主要是因为在 Firefox 里面getYear返回的是 "当前年份-1900" 的值。

      【兼容办法】:解决办法是加上对年份的判断,如:

    1 var year= new Date().getYear();
    2 year = (year<1900?(1900+year):year);
    3 document.write(year);

      也可以通过 getFullYear getUTCFullYear去调用:

    1 var year = new Date().getFullYear();
    2 
    3 document.write(year); 

    7.document.all 

      【问题描述】:document.allIESafari下都可以使用,firefoxChrome下不能使用

      【兼容办法】:所有以document.all.*方法获取对象的地方都改为document.getElementByIddocument.getElementsByNamedocument.getElementsByTagName

    8.变量名与对象ID相同的问题

      【问题描述】:IE,HTML对象的ID可以作为document的下属对象变量名直接使用,如下面的写法:

    objid.value = “123”;//objid为控件ID

      其它浏览器下则不能这样写。原因是其它浏览器下,可以使用与HTML对象ID相同的变量名,IE下则不能。

      【兼容办法】:使用document.getElementById(idName)等通用方法先获取对象,再操行其它操作。如: 

    document.getElementById(objid).value = “123”; //objid为控件ID

      注:最好不要取HTML对象ID相同的变量名,以减少错误;在声明变量时,一律加上var,以避免歧义。

    9.select元素的add方法

      【问题描述】:IESafariChrome下,select控件添加项时使用如下的方法:

    document.getElementById(“select1”).add(new Options(“aa”,”aa”));

      但在FireFox下这样操作会报错。

      【兼容办法】:统一使用兼容方法,加options属性,如下:

    document.getElementById(“select1”).options.add(new Options(“aa”,”aa”));

    10.html元素的自定义属性

      【问题描述】:IE下元素属性访问方法如document.getElementById(id).属性名,而且对于自定义属性和非自定义属性均有效。但在其它浏览器下该方法只适应于元素的公共属性,自定义属性则取不到。

      【兼容办法】:jQuery的方法来取,如$(“#id”).attr(“属性”)或用document.getElementById(id).getAttribute(“属性”),两种方法都可以适用所有浏览器。

    11.html元素innerText属性

      【问题描述】:取元素文本的属性innerTextIE中能正常工作,但此属性不是DHTML标准,其它浏览器不支持,其它浏览器中使用textContent属性获取。 

      【兼容办法】:

      (1)通用方法是用jQuery方法$(“#id”).text(),如:

    //document.getElementById(id).innerText;
    
    $(“#id”).text();

      (2)取值前判断浏览器,根据具体情况取值,如:

    var obj = document.getElementById(id);
    
    var str = (obj.innerText)?obj.innerText:obj.textContent;

      (3)也可以通过原型扩展方法来为元素添加innerText,扩展方法如下:

    复制代码
    if(typeof(HTMLElement)!="undefined" && !window.opera) 
    { 
        var pro = window.HTMLElement.prototype;     
    
        pro.__defineGetter__("innerText",function (){ 
            var anyString = ""; 
            var childS = this.childNodes; 
            for(var i=0; i<childS.length; i++) 
            { 
                if(childS[i].nodeType==1)
                { 
                    anyString += childS[i].tagName=="BR" ? '
    ' : childS[i].innerText; 
                }
                else if(childS[i].nodeType==3) 
                {
                    anyString += childS[i].nodeValue; 
                }
            } 
            return anyString; 
         }); 
    
         pro.__defineSetter__("innerText",function(sText){
            this.textContent=sText; 
         });   
    }
    复制代码

    12.html元素innerHTMLouterHTML属性 

      【问题描述】:innerHTML是所有浏览器都支持的属性。outerHTML属性不是DHTML标准,IE外的其它浏览器不支持。

      【兼容办法】:在非IE浏览器下必须使用扩展方法才能获取,扩展方法如下: 

    复制代码
    if(typeof(HTMLElement)!="undefined" && !window.opera) 
    { 
         var pro = window.HTMLElement.prototype;     
         pro.__defineGetter__("outerHTML", function(){     
              var str = "<" + this.tagName;     
              var a = this.attributes;     
              for(var i = 0, len = a.length; i < len; i++)
              {     
                   if(a[i].specified)
                   {     
                        str += " " + a[i].name + '="' + a[i].value + '"';     
                   }     
              }     
              if(!this.canHaveChildren)
              {     
                   return str + " />";     
              }     
              return str + ">" + this.innerHTML + "</" + this.tagName + ">";     
         });     
    
          pro.__defineSetter__("outerHTML", function(s){     
              var r = this.ownerDocument.createRange();     
              r.setStartBefore(this);     
              var df = r.createContextualFragment(s);     
              this.parentNode.replaceChild(df, this);     
              return s;     
         });     
    }
    复制代码

    13.html元素parentElement属性

      【问题描述】:parentElement是取元素父结点的属性,此属性只有IE支持,其它浏览器均不支持。

      【兼容办法】:parentNode属性来获取父结点,如:

    //document.getElementById(id).parentElement;
    
    document.getElementById(id).parentNode;

    14.集合类对象问题

      【问题描述】:IE下对于集合类对象,如forms,frames,可以使用()[]获取集合类对象,SafariChrome也都支持,如

    document.forms(“formid”)  document.forms[“formid”]。Firefox,只能使用[]获取集合类对象。

      【兼容办法】:统一使用[]获取集合类对象,如: 

    document.forms[0];
    
    document.forms[“formid”];

      【注】:所有以数组方式存储的对象都在访问子成员时,都必须以[]方式索引得到,如常见的XML文档遍历,也需要改,如下: 

    // xmldoc.documentElement.childNodes(1) 
    
    xmldoc.documentElement.childNodes[1]

    15.frame操作

      【问题描述】:IE、SafariChrome下,用window对象访问frame对象时,可以用idname属性来获取,如

    window.frameId;
    window.frameName;

      但在firefox下,必须使用frame对象的name属性才能获取到。

      【兼容办法】:

      (1)访问frame对象:统一使用window.document.getElementById(frameId)来访问这个frame对象。

      (2)切换frame内容:统一使用window.document.getElementById(testFrame).src=xxx.htm切换。 

      如果需要将frame中的参数传回父窗口,可以在frame中使用parent来访问父窗口。例如:parent.document.form1.filename.value=Aqing; 

      (3)iframe页中的对象: $("#frameid").contents().find("#html控件id") 

      (4)iframe页中的iframe: $("#frameid").contents().find("#frameid1").contents(); 

      (5)iframe中的方法或变量:$("#frameid")[0] .contentWindow.SaveFile("false", strRet, a); 

    16.insertAdjacentHTMLinsertAdjacentText

      【问题描述】:insertAdjacentHTML 方法是比 innerHTMLouterHTML 属性更灵活的插入 HTML 代码的方法。它可以实现在一个 DOM 元素的前面、后面、第一个子元素前面、最后一个子元素后面四个位置,插入指定的 HTML 代码。不是 W3C 标准的 DOM 方法,W3C 近期在 HTML5 草案中扩展了这个方法。

      insertAdjacentText 是比 innerTextouterText 属性更灵活的插入文本的方法。它可以实现在一个 DOM 元素的前面、后面、第一个子元素前面、最后一个子元素后面四个位置,插入指定的文本。不是 W3C 标准的 DOM 方法,至今为止 W3C  HTML5还未涉及此方法。 

      insertAdjacentHTMLinsertAdjacentText可以IESafariChrome上执行,只有FireFox不支持, 

      【兼容办法】:可用以下方法进行扩展: 

    复制代码
    if (typeof(HTMLElement) != "undefined") 
    {
        HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode) 
        {
            switch (where) 
            {
                case "beforeBegin":
                    this.parentNode.insertBefore(parsedNode, this);
                    break;
                case "afterBegin":
                    this.insertBefore(parsedNode, this.firstChild);
                    break;
                case "beforeEnd":
                    this.appendChild(parsedNode);
                    break;
                case "afterEnd":
                    if (this.nextSibling)
                        this.parentNode.insertBefore(parsedNode, this.nextSibling);
                    else
                        this.parentNode.appendChild(parsedNode);
                    break;
            }
        }
        HTMLElement.prototype.insertAdjacentHTML = function(where, htmlStr) 
        {
            var r = this.ownerDocument.createRange();
            r.setStartBefore(this);
            var parsedHTML = r.createContextualFragment(htmlStr);
            this.insertAdjacentElement(where, parsedHTML);
        }
    
        HTMLElement.prototype.insertAdjacentText = function(where, txtStr) 
        {
            var parsedText = document.createTextNode(txtStr);
            this.insertAdjacentElement(where, parsedText);
        }
    }         
    复制代码

    17.Html元素的children属性

      【问题描述】:children是取HTML元素子结点的属性,只有IE下支持,其它浏览器下用childNodes 

      【兼容办法】:统一改为用childNodes属性取子结点。或用以下方法扩展HTML元素的属性: 

    复制代码
    if (typeof(HTMLElement) != "undefined") 
    {
        HTMLElement.prototype.__defineGetter__("children",function(){ 
             var returnValue = new Object(); 
             var number = 0; 
             for(var i=0; i<this.childNodes.length; i++)
             { 
                 if(this.childNodes[i].nodeType == 1)
                 { 
                     returnValue[number] = this.childNodes[i]; 
                     number++; 
                 } 
             } 
             returnValue.length = number; 
             return returnValue; 
        })
    } 
    复制代码

    18.insertRowinserCell 

      【问题描述】:insertRowinsertCell是在表格中插入行或插入列的方法,在IE中使用方法如下 

    var nowTB = document.getElementById("tb1");
    nowTR = nowTB.insertRow();
    nowTD = nowTR.insertCell();  

      SafariChrome下也可以正常执行,但插入行的位置不一样IE下默认在表尾插入行,SafariChrome默认在表头插入行;但在FireFox下调用会报错。 

      【兼容办法】:下面的方法可以在所有浏览器上调用,而且插入行的位置都是表尾,不同之处就是执行前传递一个默认值。推荐使用。 

    var nowTB = document.getElementById("tb1");
    
    nowTR = nowTB.insertRow(-1);
    
    nowTD = nowTR.insertCell(-1);

    19.document.createElement

      【问题描述】:IE3种方式都可以创建一个元素:

    1 document.createElement("<input type=text>")
    
    2 document.createElement("<input>")
    
    3 document.createElement("input")

      SafariFireFoxChrome只支持一种方式:

    document.createElement("input");
    
    document.setAttribute(name,value);

      【兼容办法】:统一使用所有浏览器都支持的方法,如下:

    document.createElement("input");
    
    document.setAttribute(name,value);

    20.浏览器处理childNodes的异同

      【问题描述】:如下HTML代码:

    复制代码
    <ul id="main">
    
    <li>1</li>
    
    <li>2</li>
    
    <li>3</li>
    
    </ul>
    
    <input type=button value="click me!" onclick=
    
    "alert(document.getElementById('main').childNodes.length)">
    复制代码

      分别用IE和其它浏览器运行,IE的结果是3,而其它则是7

      IE是将一个完整标签作为一个节点,而SafariFireFoxChrome除了上述的的情况外,也把一个标签的结束符“>”到下一个标签的起始符“<”之间的内容(除注释外,包括任何的文字、空格、回车、制表符)也算是一个节点了,而且这种节点也有它们自己独特的属性和值nodeName="#text"

      【兼容办法】:在实际运用中,SafariFireFoxChrome在遍历子节点时,在for循环里加上

      if(childNode.nodeName=="#text") continue;或者nodeType == 1 这样,便跳过不需要的操作,使程序运行的更有效率。也可以用node.getElementsByTagName()回避。

    21.document.getElementsByName

      【问题描述】:在元素只有name属性,没有id属性的情况下,在IE中获取不到DIV元素,其它浏览器可以获取。当前nameid属性都存在时,所有浏览器都可以获取到DIV元素。 

      【兼容办法】:尽量用ID来获取。 

    22.tr操作

      【问题描述】:IEtable中无论是用innerHTML还是appendChild插入<tr>都没有效果,因为在IE浏览器下tr是只读的。而其他浏览器下可以这样操作。 

      【兼容办法】:<tr>加到table<tbody>元素中,如下面所示:

    复制代码
    var row = document.createElement("tr");
    
    var cell = document.createElement("td");
    
    var cell_text = document.createTextNode("插入的内容");
    
    cell.appendChild(cell_text);
    
    row.appendChild(cell);
    
    document.getElementsByTagName("tbody")[0].appendChild(row);
    复制代码

    23.移除节点removeNode() 

      【问题描述】:appendNodeIE和其它浏览器下都能正常使用,但是removeNode只能在IE下用。removeNode方法的功能是删除一个节点,语法为node.removeNodefalse)或者node.removeNodetrue),返回值是被删除的节点。

      removeNodefalse)表示仅仅删除指定节点,然后这个节点的原孩子节点提升为原双亲节点的孩子节点。

      removeNodetrue)表示删除指定节点及其所有下属节点。被删除的节点成为了孤立节点,不再具有有孩子节点和双亲节点。 

      【兼容办法】:兼容IE和其它浏览器的方法是removeChild,先回到父节点,在从父节点上移除要移除的节点。

    // 为了在IE和其它浏览器下都能正常使用,取上一层的父结点,然后remove。
    node.parentNode.removeChild(node);

    24.expression

      【问题描述】:IE下样式支持计算表达式expression,但其它浏览器不支持,而且IE以后高版本也可能不再支持这种样式,所以不允许使用。下面是通常使用的情况:

    <div id=”content”
     style=’height:expression(document.body.offsetHeight-80)”></div>

      【兼容办法】:去掉样式设置,将其写到函数中,分别在页面加载完毕和页面尺寸发生变化时执行。如下:

    复制代码
    $(function(){
      $(“#content”).height($(document.body).height()-80);
    })
    
    $(window).resize(function(){
      $(“#content”).height($(document.body).height()-80);
    });
    复制代码

    25.Cursor

      【问题描述】:Cursorhand属性只有IE支持,其它浏览器没有效果,如: 

    <div style=”cursor:hand”></div>

      【兼容办法】:统一用pointer值,如:

    <div style=”cursor: pointer”></div>

    26.CSS透明问题

      【问题描述】:IE支持但其它浏览器不支持的透明样式如下:

    <div style="filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=20);200px;height:200px;background-color:Blue">ddddd</div>

      其它浏览器支持但IE不支持的透明样式如下:

    <div style="opacity:0.2;200px;height:200px;background-color:Blue">ddddd</div>

      【兼容办法】:利用”!important”来设置元素的样式。SafariFireFoxChrome对于”!important”会自动优先解析,然而IE则会忽略。如下

    <div style="filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=20);200px;height:200px;background-color:Blue;!important; opacity:0.2">ddddd</div>

    27.pixelHeightpixelWidth

      【问题描述】:pixelHeightpixelWidth是元素的高度和宽度样式,通常获取方法是: 

    obj.style.pixelWidth;
    
    obj.style.pixelHeight;

      IESafariChrome都支持此样式,返回的值是整数,FireFox不支持

      【兼容办法】:所有浏览器都支持obj.style.height,但返回的值是带单位的,如“100px”。可以用如下方法来获取:

    parseInt(obj.style.height)

    28.noWrap

      【问题描述】:nowrap 属性是被废弃的属性。

      【兼容办法】:使用 CSS 规则 white-space:nowrap 代替这个属性。

    29.CSSfloat属性

      【问题描述】:Javascript访问一个给定CSS 值的最基本句法是:object.style.property,但部分CSS属性跟Javascript中的保留字命名相同,如"float""for""class"等,不同浏览器写法不同。

      在IE中这样写:

    document.getElementById("header").style.styleFloat = "left";

      在其它浏览器中这样写:

    document.getElementById("header").style.cssFloat = "left";

      【兼容办法】:兼容方法是在写之前加一个判断,判断浏览器是否是IE

    if(jQuery.browser.msie){
        document.getElementById("header").style.styleFloat = "left";
    }
    else{
        document.getElementById("header").style.cssFloat = "left";
    }

    30.访问label标签中的for

      【问题描述】:for 属性规定 label 与哪个表单元素绑定。IE中这样写:

    var myObject = document.getElementById("myLabel");
    
    var myAttribute = myObject.getAttribute("htmlFor");

       在Firefox中这样写:

    var myObject = document.getElementById("myLabel");
    
    var myAttribute = myObject.getAttribute("for");

      【兼容办法】:判断浏览器是否是IE

    复制代码
    var myObject = document.getElementById("myLabel");
    if(jQuery.browser.msie){
        var myAttribute = myObject.getAttribute("htmlFor");
    }
    else{
        var myAttribute = myObject.getAttribute("for");
    }
    复制代码

    31.访问和设置class属性 

      【问题描述】:同样由于classJavascript保留字的原因,这两种浏览器使用不同的 JavaScript 方法来获取这个属性。 

      IE8.0之前的所有IE版本的写法: 

    var myObject = document.getElementById("header");
    
    var myAttribute = myObject.getAttribute("className"); 

      适用于IE8.0 以及 firefox的写法:

    var myObject = document.getElementById("header");
    
    var myAttribute = myObject.getAttribute("class");

      另外,在使用setAttribute()设置Class属性的时候,两种浏览器也存在同样的差异。

      setAttribute("className",value);这种写法适用于IE8.0之前的所有IE版本,注意:IE8.0也不支持"className"属性了。setAttribute("class",value);适用于IE8.0 以及 firefox

      【兼容办法】:

      1.两种都写上:

    复制代码
    1 //设置header的class为classValue
    2 var myObject = document.getElementById("header");
    3 
    4 myObject.setAttribute("class","classValue");
    5 
    6 myObject.setAttribute("className","classValue");
    复制代码

       2.IEFF都支持object.className,所以可以这样写: 

    var myObject = document.getElementById("header");
    
    myObject.className="classValue";//设置header的class为classValue

      3.先判断浏览器类型,再根据浏览器类型采用对应的写法。 

    32.对象宽高赋值问题

      【问题描述】:IE浏览器中中类似 obj.style.height = imgObj.height 的语句无效,必须加上’px’

      【兼容办法】:给元素高度宽度附值是,统一都加上’px’,如: 

    obj.style.height = imgObj.height + ‘px’;

    33.鼠标位置

      【问题描述】:IE下,even对象有xy属性,但是没有pageXpageY属性;Firefox下,even对象有pageXpageY属性,但是没有xy属性;SafariChrome中xy属性和pageXpageY都有。

      【兼容办法】:使用mX = event.x ? event.x : event.pageX;来代替。复杂点还要考虑绝对位置。 

    复制代码
    function getAbsPoint(e){
        var x = e.offsetLeft, y = e.offsetTop;
        while (e = e.offsetParent) {
            x += e.offsetLeft;
            y += e.offsetTop;
        }
        alert("x:" + x + "," + "y:" + y);
    }    
    复制代码

    34.event.srcElement 

      【问题描述】:IE下,event对象有srcElement属性,但是没有target属性;其它浏览器下,even对象有target属性,但是没有srcElement属性。

      【兼容办法】:

    var obj = event.srcElement?event.srcElement:event.target;

    35.关于<input type="file">

      (1) safari浏览器下的此控件没有文本框,只有一个“选取文件”的按钮,所有也没有onblur事件,如果在<input type="file" onblur="alert(0);">中用到了需要做特殊处理。

      (2) FF浏览器下用<input type="file" name="file"> 上传文件后取file.value时只能去掉文件名而没有文件路径,不能实现预览的效果,可以用document.getElementById("pic").files[0].getAsDataURL();取到加密后的路径,此路径只有在FF下才可以解析。

      (3) safari浏览器下用<input type="file" name="file"> 上传文件后取file.value时只能去掉文件名而没有文件路径,不能实现预览的效果。建议使用上传后的路径预览。

    36.jquery对象是否为空

      jquery对象是否为空判断,用length判断一下

    $("#hidTitle").length>0

      IOS设计模式浅析

    数据结构和算法之:图的深度优先和广度优先遍历及其Java实现

     

    图的遍历,所谓遍历,即是对结点的访问。

    一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略,深度优先遍历和广度优先遍历。
     
    深度优先遍历:
    深度优先遍历,从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点。总结起来可以这样说:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
    我们从这里可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
     
    具体算法表述如下:
    1,访问初始结点v,并标记结点v为已访问。
    2,查找结点v的第一个邻接结点w。
    3,若w存在,则继续执行4,否则算法结束。
    4,若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)。
    5,查找结点v的w邻接结点的下一个邻接结点,转到步骤3。
     
    例如下图,其深度优先遍历顺序为 1->2->4->8->5->3->6->7
     
    广度优先遍历:
    类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点。
     
    具体算法表述如下:
    1,访问初始结点v并标记结点v为已访问。
    2,结点v入队列
    3,当队列非空时,继续执行,否则算法结束。
    4,出队列,取得队头结点u。
    5,查找结点u的第一个邻接结点w。
    6,若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:
    ① 若结点w尚未被访问,则访问结点w并标记为已访问。
    ②结点w入队列
    ③查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6。
     
    如上文的图,其广度优先算法的遍历顺序为:1->2->3->4->5->6->7->8
     
    深度优先遍历和广度优先遍历的Java实现
     
    前面一文http://www.cnblogs.com/breakpoint/p/3477703.html已经实现过邻接矩阵图类AMWGraph.java,在这里还是把这个类再贴一遍好了。在原先类的基础上增加了两个遍历的函数,分别是depthFirstSearch()和broadFirstSearch()。
    复制代码
      1 package com.ds;
      2 
      3 import java.util.ArrayList;
      4 import java.util.LinkedList;
      5 
      6 /**
      7  * @description 图的邻接矩阵图类
      8  * @author 等待飞鱼
      9  * @time 2013.12.17 
     10  */
     11 public class AMWGraph {
     12 
     13     private ArrayList vertexList;//存储点的链表
     14     private int[][] edges;//邻接矩阵,用来存储边
     15     private int numOfEdges;//边的数目
     16     
     17     public AMWGraph(int n)
     18     {
     19         //初始化矩阵,一维数组,和边的数目
     20         edges=new int[n][n];
     21         vertexList=new ArrayList(n);
     22         numOfEdges=0;
     23     }
     24     //得到结点的个数
     25     public int getNumOfVertex(){
     26         return vertexList.size();
     27     }
     28     //得到边的数目
     29     public int getNumOfEdges()
     30     {
     31         return numOfEdges;
     32     }
     33     //返回结点i的数据
     34     public Object getValueByIndex(int i)
     35     {
     36         return vertexList.get(i);
     37     }
     38     //返回v1,v2的权值
     39     public int getWeight(int v1,int v2)
     40     {
     41         return edges[v1][v2];
     42     }
     43     //插入结点
     44     public void insertVertex(Object vertex)
     45     {
     46         vertexList.add(vertexList.size(),vertex);
     47     }
     48     //插入结点
     49     public void insertEdge(int v1,int v2,int weight){
     50         edges[v1][v2]=weight;
     51         numOfEdges++;
     52     }
     53     //删除结点
     54     public void deleteEdge(int v1,int v2)
     55     {
     56         edges[v1][v2]=0;
     57         numOfEdges--;
     58     }
     59     //得到第一个邻接结点的下标
     60     public int getFirstNeighbor(int index)
     61     {
     62         for(int j=0;j<vertexList.size();j++)
     63         {
     64             if (edges[index][j]>0)
     65             {
     66                 return j;
     67             }
     68         }
     69         return -1;
     70     }
     71     //根据前一个邻接结点的下标来取得下一个邻接结点
     72     public int getNextNeighbor(int v1,int v2)
     73     {
     74         for (int j=v2+1;j<vertexList.size();j++)
     75         {
     76             if (edges[v1][j]>0)
     77             {
     78                 return j;
     79             }
     80         }
     81         return -1;
     82     }
     83     //私有函数,深度优先遍历
     84     private void depthFirstSearch(boolean[] isVisited,int  i)
     85     {
     86         //首先访问该结点,在控制台打印出来
     87         System.out.print(getValueByIndex(i)+"  ");
     88         //置该结点为已访问
     89         isVisited[i]=true;
     90         
     91         int w=getFirstNeighbor(i);//
     92         while (w!=-1)
     93         {
     94             if (!isVisited[w])
     95             {
     96                 depthFirstSearch(isVisited,w);
     97             }
     98             w=getNextNeighbor(i, w);
     99         }
    100     }
    101     //对外公开函数,深度优先遍历,与其同名私有函数属于方法重载
    102     public void depthFirstSearch()
    103     {
    104         boolean[] isVisited=new boolean[getNumOfVertex()];//记录结点是否已经被访问的数组
    105         for (int i=0;i<getNumOfVertex();i++)
    106         {
    107             isVisited[i]=false;//把所有节点设置为未访问
    108         }
    109         for(int i=0;i<getNumOfVertex();i++)
    110         {
    111             //因为对于非连通图来说,并不是通过一个结点就一定可以遍历所有结点的。
    112             if (!isVisited[i]){
    113                 depthFirstSearch(isVisited,i);
    114             }
    115         }
    116     }
    117     //私有函数,广度优先遍历
    118     private void broadFirstSearch(boolean[] isVisited,int i)
    119     {
    120         int u,w;
    121         LinkedList queue=new LinkedList();
    122         
    123         //访问结点i
    124         System.out.print(getValueByIndex(i)+"  ");
    125         isVisited[i]=true;
    126         //结点入队列
    127         queue.addlast(i);
    128         while (!queue.isEmpty())
    129         {
    130             u=((Integer)queue.removeFirst()).intValue();
    131             w=getFirstNeighbor(u);
    132             while(w!=-1){
    133                 if(!isVisited[w]){
    134                         //访问该结点
    135                         System.out.print(getValueByIndex(w)+"  ");
    136                         //标记已被访问
    137                         isVisited[w]=true;
    138                         //入队列
    139                         queue.addLast(w);
    140                 }
    141                 //寻找下一个邻接结点
    142                 w=getNextNeighbor(u, w);
    143             }
    144         }
    145     }
    146     //对外公开函数,广度优先遍历
    147     public void broadFirstSearch()
    148     {
    149         boolean[] isVisited=new boolean[getNumOfVertex()];
    150         for (int i=0;i<getNumOfVertex();i++)
    151         {
    152             isVisited[i]=false;
    153         }
    154         for(int i=0;i<getNumOfVertex();i++)
    155         {
    156             if(!isVisited[i])
    157             {
    158                 broadFirstSearch(isVisited, i);
    159             }
    160         }
    161     }
    162 }
    复制代码

    上面的public声明的depthFirstSearch()和broadFirstSearch()函数,是为了应对当该图是非连通图的情况,如果是非连通图,那么只通过一个结点是无法完全遍历所有结点的。

    下面根据上面用来举例的图来构造测试类:

    复制代码
    package com.ds;
    
    public class TestSearch {
    
        public static void main(String args[])
        {
            int n=8,e=9;//分别代表结点个数和边的数目
            String labels[]={"1","2","3","4","5","6","7","8"};//结点的标识
            AMWGraph graph=new AMWGraph(n);
            for(String label:labels){
                graph.insertVertex(label);//插入结点
            }
            //插入九条边
            graph.insertEdge(0, 1, 1);
            graph.insertEdge(0, 2, 1);
            graph.insertEdge(1, 3, 1);
            graph.insertEdge(1, 4, 1);
            graph.insertEdge(3, 7, 1);
            graph.insertEdge(4, 7, 1);
            graph.insertEdge(2, 5, 1);
            graph.insertEdge(2, 6, 1);
            graph.insertEdge(5, 6, 1);
            graph.insertEdge(1, 0, 1);
            graph.insertEdge(2, 0, 1);
            graph.insertEdge(3, 1, 1);
            graph.insertEdge(4, 1, 1);
            graph.insertEdge(7, 3, 1);
            graph.insertEdge(7, 4, 1);
            graph.insertEdge(4, 2, 1);
            graph.insertEdge(5, 2, 1);
            graph.insertEdge(6, 5, 1);
            
            System.out.println("深度优先搜索序列为:");
            graph.depthFirstSearch();
            System.out.println();
            System.out.println("广度优先搜索序列为:");
            graph.broadFirstSearch();
        }
    }
    复制代码

    运行后控制台输出如下

    如果有什么想说的,请给我留言,谢谢!

     
     
     
    标签: 算法
    循自然之道,抚浮躁之心
  • 相关阅读:
    java 配置Apache,Tomcat的gzip压缩功能
    java并发编程中CountDownLatch和CyclicBarrier的使用
    Java常用工具包 Jodd
    Servlet 3特性:异步Servlet
    tomcat7安装
    2013-Hessian
    使用commons configuration管理配置文件
    2013-JVisualVM远程监听服务器内存进程
    Jquery复选框
    Jquery选择器总结
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3479415.html
Copyright © 2011-2022 走看看