前言
阅读本文之前,您也可以到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)是一种能够做以下工作的对象:
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.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注解属性来修饰类。如果有注解属性,则成员均被忽略,除非有DataMember。DataMember也可以序列化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 }
只读属性默认是序列化的。
默认情况下,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;
为了书写有缩进的JSON,可以将Formatting设置为Formatting.Indented:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
为了在不修改数据模型的情况下,用驼峰式大小写转换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。
本小节描述使用默认DataContractSerializer的时,XML格式化器的一些特殊行为。默认情况下,DataContractSerializer行为如下:
如果需要在序列化上的更多控制,可以用DataContract注解属性修饰类。当这个注解属性出现时,该类按以策略序列化:
只读属性是不被序列化的。如果只读属性有一个支撑private字段,可以用DataMember注解属性对这个private字段进行标记。这种办法需要在类上使用DataContract注解属性。
[DataContract] public class Product { [DataMember] private int pcode; // serialized(序列化的) // Not serialized (read-only) // 不作序列化(只读) public int ProductCode { get { return pcode; } } }
日期被写成ISO 8601格式。例如,“2012-05-23T20:21:37.9116538Z”。
要书写缩进的XML,请将Indent属性设置为true:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.Indent = true;
你可以为不同的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格式化器,只要你不想使用它们。这么做的主要原因是:
以下代码演示了如何删除默认的格式化器。在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、FireFox、Chrome,引入了jQuery框架进行改造后,大部分功能可以正常使用,现将总结分享一下。
1.eval(idName)
【问题描述】:IE、safari、Chrome浏览器下都可以使用eval(idName)或getElementById(idName)来取得id为idName的HTML对象;firefox下只能使用getElementById(idName)来取得id为idName的HTML对象.
【兼容办法】:统一用getElementById("idName")来取得id为idName的HTML对象。
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支持,其它浏览器均不支持。查找节点是常用的方法有selectNodes和selectSingleNode,这两个方法也只有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对象添加selectNodes和selectSingleNode方法,扩展方法如下:
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支持此方法,Safari、FireFox、Chrome都不支持,使用时会提示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.all在IE、Safari下都可以使用,firefox、Chrome下不能使用
【兼容办法】:所有以document.all.*方法获取对象的地方都改为document.getElementById或document.getElementsByName或document.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方法
【问题描述】:在IE,Safari,Chrome下,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属性
【问题描述】:取元素文本的属性innerText在IE中能正常工作,但此属性不是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" ? '\n' : 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元素innerHTML、outerHTML属性
【问题描述】: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等,可以使用()或[]获取集合类对象,Safari,Chrome也都支持,如
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、Safari、Chrome下,用window对象访问frame对象时,可以用id和name属性来获取,如
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.insertAdjacentHTML和insertAdjacentText
【问题描述】:insertAdjacentHTML 方法是比 innerHTML、outerHTML 属性更灵活的插入 HTML 代码的方法。它可以实现在一个 DOM 元素的前面、后面、第一个子元素前面、最后一个子元素后面四个位置,插入指定的 HTML 代码。不是 W3C 标准的 DOM 方法,W3C 近期在 HTML5 草案中扩展了这个方法。
insertAdjacentText 是比 innerText、outerText 属性更灵活的插入文本的方法。它可以实现在一个 DOM 元素的前面、后面、第一个子元素前面、最后一个子元素后面四个位置,插入指定的文本。不是 W3C 标准的 DOM 方法,至今为止 W3C 的 HTML5还未涉及此方法。
insertAdjacentHTML和insertAdjacentText可以IE、Safari、Chrome上执行,只有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.insertRow\inserCell
【问题描述】:insertRow和insertCell是在表格中插入行或插入列的方法,在IE中使用方法如下
var nowTB = document.getElementById("tb1"); nowTR = nowTB.insertRow(); nowTD = nowTR.insertCell();
Safari、Chrome下也可以正常执行,但插入行的位置不一样IE下默认在表尾插入行,Safari、Chrome默认在表头插入行;但在FireFox下调用会报错。
【兼容办法】:下面的方法可以在所有浏览器上调用,而且插入行的位置都是表尾,不同之处就是执行前传递一个默认值。推荐使用。
var nowTB = document.getElementById("tb1"); nowTR = nowTB.insertRow(-1); nowTD = nowTR.insertCell(-1);
19.document.createElement
【问题描述】:IE有3种方式都可以创建一个元素:
1 document.createElement("<input type=text>") 2 document.createElement("<input>") 3 document.createElement("input")
Safari、FireFox、Chrome只支持一种方式:
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是将一个完整标签作为一个节点,而Safari、FireFox、Chrome除了上述的的情况外,也把一个标签的结束符“>”到下一个标签的起始符“<”之间的内容(除注释外,包括任何的文字、空格、回车、制表符)也算是一个节点了,而且这种节点也有它们自己独特的属性和值nodeName="#text"。
【兼容办法】:在实际运用中,Safari、FireFox、Chrome在遍历子节点时,在for循环里加上
if(childNode.nodeName=="#text") continue;或者nodeType == 1 这样,便跳过不需要的操作,使程序运行的更有效率。也可以用node.getElementsByTagName()回避。
21.document.getElementsByName
【问题描述】:在元素只有name属性,没有id属性的情况下,在IE中获取不到DIV元素,其它浏览器可以获取。当前name和id属性都存在时,所有浏览器都可以获取到DIV元素。
【兼容办法】:尽量用ID来获取。
22.tr操作
【问题描述】:IE下table中无论是用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()
【问题描述】:appendNode在IE和其它浏览器下都能正常使用,但是removeNode只能在IE下用。removeNode方法的功能是删除一个节点,语法为node.removeNode(false)或者node.removeNode(true),返回值是被删除的节点。
removeNode(false)表示仅仅删除指定节点,然后这个节点的原孩子节点提升为原双亲节点的孩子节点。
removeNode(true)表示删除指定节点及其所有下属节点。被删除的节点成为了孤立节点,不再具有有孩子节点和双亲节点。
【兼容办法】:兼容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
【问题描述】:Cursor的hand属性只有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);width:200px;height:200px;background-color:Blue">ddddd</div>
其它浏览器支持但IE不支持的透明样式如下:
<div style="opacity:0.2;width:200px;height:200px;background-color:Blue">ddddd</div>
【兼容办法】:利用”!important”来设置元素的样式。Safari,FireFox,Chrome对于”!important”会自动优先解析,然而IE则会忽略。如下
<div style="filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=20);width:200px;height:200px;background-color:Blue;!important; opacity:0.2">ddddd</div>
27.pixelHeight\pixelWidth
【问题描述】:pixelHeight\pixelWidth是元素的高度和宽度样式,通常获取方法是:
obj.style.pixelWidth;
obj.style.pixelHeight;
IE,Safari,Chrome都支持此样式,返回的值是整数,FireFox不支持
【兼容办法】:所有浏览器都支持obj.style.height,但返回的值是带单位的,如“100px”。可以用如下方法来获取:
parseInt(obj.style.height)
28.noWrap
【问题描述】:nowrap 属性是被废弃的属性。
【兼容办法】:使用 CSS 规则 white-space:nowrap 代替这个属性。
29.CSS的float属性
【问题描述】: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属性
【问题描述】:同样由于class是Javascript保留字的原因,这两种浏览器使用不同的 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.IE和FF都支持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对象有x、y属性,但是没有pageX、pageY属性;Firefox下,even对象有pageX、pageY属性,但是没有x、y属性;Safari、Chrome中x、y属性和pageX、pageY都有。
【兼容办法】:使用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
图的遍历,所谓遍历,即是对结点的访问。
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(); } }
运行后控制台输出如下
如果有什么想说的,请给我留言,谢谢!