临时接到这个任务,按我的习惯查到不少这种实现,感谢网上的大牛,每次都能够给我灵感
http://blog.csdn.net/gdjlc/article/details/11374787
由于我这边没有 scchema uri所以用了他这里一段精简版
/// <summary>
/// 通过xsd验证xml格式是否正确,正确返回空字符串,错误返回提示
/// </summary>
/// <param name="xmlText">xml文本内容</param>
/// <param name="schemaFile">xsd文件</param>
/// <returns></returns>
public static string XmlValidateByXsd(string xmlText, string schemaFile)
{
StringBuilder sb = new StringBuilder();
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, schemaFile);
settings.ValidationEventHandler += (x, y) =>
{
sb.AppendFormat("{0}\n", y.Message);
};
using (XmlReader reader = XmlReader.Create(new StringReader(xmlText), settings))
{
try
{
while (reader.Read()) ;
}
catch (XmlException ex)
{
sb.AppendFormat("{0}\n", ex.Message);
}
}
return sb.ToString();
}
同时为了实现动态植入,当然需要做接口代理类,但是我们的parse抽象类有两个文件的解析,dataquery和quickquery,那我照葫芦画瓢也需要对不同的泛型占位符有不同的约束 ,首先上接口和抽象类,我把具体的实现已经实现在抽象类里了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Com.Centaline.Framework.QuickQuery.Config.Entity;
using System.IO;
using System.Xml;
namespace Com.Centaline.Framework.QuickQuery.Config.Interface
{
public interface ISchemaValidator<TResult,TResource>
{
string XmlValidateByXsd(Stream xmlStream, XmlReader xmlReader);
string XmlValidateByXsd(ref TResource resource);
}
}
抽象类去实现这个接口:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Com.Centaline.Framework.Kernel.ObjectExtensions;
using Com.Centaline.Framework.QuickQuery.Config.Entity;
using Com.Centaline.Framework.QuickQuery.Config.Interface;
namespace Com.Centaline.Framework.QuickQuery.Config
{
public abstract class SchemaValidator<TResult,TResource> : ISchemaValidator<TResult, TResource>
where TResource:Resource<TResult>
where TResult : class, new()
{
public virtual string XmlValidateByXsd(Stream xmlStream, XmlReader xmlReader)
{
StringBuilder sb = new StringBuilder();
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(null, xmlReader);
settings.ValidationEventHandler += (x, y) =>
{
sb.AppendFormat("{0}\n", y.Message);
};
using (XmlReader reader = XmlReader.Create(xmlStream, settings))
{
try
{
while (reader.Read());
}
catch (XmlException ex)
{
sb.AppendFormat("{0}\n", ex.Message);
}
}
return sb.ToString();
}
public virtual string XmlValidateByXsd(ref TResource resource)
{
throw new NotImplementedException();
//return XmlValidateByXsd(resource.Stream, "d:\\Users\\wanglin36\\centaline-infrastructure\\Com.Centaline.Framework.QuickQuery\\Config\\Schema\\DataQuery.xsd");
}
}
}
这里的神奇之处我方法都用了virtual,而且对一些外部的调用类并没有实现,而且将操作的入参和出参都做了约束,这个约束要在后来具体继承给定。然后两个实现类:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Com.Centaline.Framework.QuickQuery.Config;
using Com.Centaline.Framework.QuickQuery.Config.Interface;
using Com.Centaline.Framework.QuickQuery.Config.Entity;
namespace Com.Centaline.Framework.QuickQuery.Config
{
public class QuickQuerySchemaValidator:SchemaValidator<Entity.QuickQuery, QuickQueryResource>
{
public override string XmlValidateByXsd(Stream xmlStream, XmlReader xmlReader)
{
return base.XmlValidateByXsd(xmlStream, xmlReader);
}
public override string XmlValidateByXsd(ref QuickQueryResource resource)
{
using (var sm = Assembly.GetExecutingAssembly().GetManifestResourceStream("Com.Centaline.Framework.QuickQuery.Config.Schema.QuickQuery.xsd"))
{
//byte[] bs = new byte[sm.Length];
//sm.Read(bs, 0, (int)sm.Length);
return XmlValidateByXsd(resource.Stream, XmlReader.Create(sm));
}
//return XmlValidateByXsd(resource.Stream, "d:\\Users\\wanglin36\\centaline-infrastructure\\Com.Centaline.Framework.QuickQuery\\Config\\Schema\\QuickQuery.xsd");
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Com.Centaline.Framework.QuickQuery.Config.Entity;
namespace Com.Centaline.Framework.QuickQuery.Config
{
public class QuerySchemaValidator : SchemaValidator<Entity.Query, QueryResource>
{
public override string XmlValidateByXsd(Stream xmlStream, XmlReader xmlReader)
{
return base.XmlValidateByXsd(xmlStream, xmlReader);
}
public override string XmlValidateByXsd(ref QueryResource resource)
{
using (var sm = Assembly.GetExecutingAssembly().GetManifestResourceStream("Com.Centaline.Framework.QuickQuery.Config.Schema.DataQuery.xsd"))
{ byte[] bs = new byte[sm.Length];
//sm.Read(bs, 0, (int)sm.Length);
//sm.Close();
return XmlValidateByXsd(resource.Stream, XmlReader.Create(sm));
}
// return XmlValidateByXsd(resource.Stream, "d:\\Users\\wanglin36\\centaline-infrastructure\\Com.Centaline.Framework.QuickQuery\\Config\\Schema\\DataQuery.xsd");
}
}
}
这里最有意思的是我知道xsd架构文件是嵌入在dll的中间插件里,然后用GetExecutingAssembly来读到assemble的stream用xmlreader 来创建,这都归功于网络上的高手:
http://blog.csdn.net/u011190993/article/details/39960865
看了上面的博客,我在结合msdn发觉XmlSchemaSet.add有Xmlreader作为schema文件的输入:
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=ZH-CN&k=k(System.Xml.Schema.XmlSchemaSet.Add);k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5);k(DevLang-csharp)&rd=true
最后用spring.net的属性植入来植入这两个实现类,根据接口,竟然都能够找到对应的实现,真是发觉java spring的强大
<object id="IQueryParser" type="Com.Centaline.Framework.QuickQuery.Config.QueryParser">
<property name="ConfigReader">
<object type="Com.Centaline.Framework.QuickQuery.Config.FileQueryConfigReader">
<property name="PatchBuilder" ref="IFileQueryResourceRelativePatchBuilder"></property>
</object>
</property>
<property name="ConfigParser">
<!--<object type="Com.Centaline.Framework.QuickQuery.Config.XmlQueryConfigParser"/>-->
<object type="Com.Centaline.Framework.QuickQuery.Config.XmlReadQueryConfigParser"/>
</property>
<property name="ConfigParseCacheProvider" ref="IQueryConfigParseCacheProvider">
</property>
<property name="ConfigResourceReader">
<object type="Com.Centaline.Framework.QuickQuery.Config.FileQueryConfigResourceReader">
<property name="PatchBuilder" ref="IFileQueryResourceRelativePatchBuilder"></property>
</object>
</property>
<property name="SchemaValidator">
<object type="Com.Centaline.Framework.QuickQuery.Config.QuerySchemaValidator"></object>
</property>
</object>
<object id="IQuickQueryConfigParseCacheProvider" type="Com.Centaline.Framework.Kernel.Caching.MemeryCacheProvider"/>
<object id="IQuickQueryParser" type="Com.Centaline.Framework.QuickQuery.Config.QuickQueryParser">
<property name="QueryParser" ref="IQueryParser"></property>
<property name="ConfigReader">
<object type="Com.Centaline.Framework.QuickQuery.Config.FileQuickQueryConfigReader">
<property name="PatchBuilder" ref="IFileQueryResourceRelativePatchBuilder"></property>
</object>
</property>
<property name="ConfigParser">
<!--<object type="Com.Centaline.Framework.QuickQuery.Config.XmlQuickQueryConfigParser"/>-->
<object type="Com.Centaline.Framework.QuickQuery.Config.XmlReadQuickQueryConfigParser"/>
</property>
<property name="ConfigParseCacheProvider" ref="IQuickQueryConfigParseCacheProvider">
</property>
<property name="ConfigResourceReader">
<object type="Com.Centaline.Framework.QuickQuery.Config.FileQuickQueryConfigResourceReader">
<property name="PatchBuilder" ref="IFileQueryResourceRelativePatchBuilder"></property>
</object>
</property>
<property name="SchemaValidator">
<object type="Com.Centaline.Framework.QuickQuery.Config.QuickQuerySchemaValidator"></object>
</property>
</object>
红色就是属性注入的地方,最后只要Parse抽象类中做定义就可以了,通过接口隐藏具体的实现实在太强大了
下面这段就是quickquery实现中加入了验证:
protected virtual TResult ParseDirect(ref TResource resource)
{
Monitor.Watch(string.Format("read and parse file {0} .", resource.Path));
TResult result = null;
resource = ConfigReader.Read(ref resource);
resource.PreLastWriteTime = resource.LastWriteTime;
string ValidateResult = SchemaValidator.XmlValidateByXsd(ref resource);
resource.Stream.Close();
resource.Stream.Dispose();
resource.Stream = null;
if (!ValidateResult.IsNullOrEmpty())
{
string errorMessage = string.Format("file : {0} does not Conform to the schema file!", resource.Path);
Monitor.Watch(errorMessage);
throw new Exception.QuickQueryException(errorMessage);
}
resource = ConfigReader.Read(ref resource);
resource.PreLastWriteTime = resource.LastWriteTime;
result = resource.Entity = ConfigParser.Parse(ref resource);
return result;
}
下面就是DataQuery实现中加入的验证:
public virtual TResult ParseSchema(ref TResource resource)
{
lock (_LockObject)
{
TResult result = new TResult();
switch (resource.ParseLevel)
{
case ParseLevel.NULL:
resource = ConfigResourceReader.Read(ref resource);
resource = ConfigReader.Read(ref resource);
string ValidateResult = SchemaValidator.XmlValidateByXsd(ref resource);
resource.Stream.Close();
resource.Stream.Dispose();
resource.Stream = null;
if (!ValidateResult.IsNullOrEmpty())
{
string errorMessage = string.Format("file : {0} does not Conform to the schema file!", resource.Path);
Monitor.Watch(errorMessage);
throw new Exception.QuickQueryException(errorMessage);
}
resource = ConfigResourceReader.Read(ref resource);
resource = ConfigReader.Read(ref resource);
result = ConfigParser.ParseSchema(ref resource);
break;
case ParseLevel.Detail:
result = resource.Entity;
break;
default:
break;
}
return result;
}
}