Không cần dùng đến giải pháp dựa trên XSLT, nếu bạn muốn sạch sẽ, trang nhã và thông minh, bạn sẽ cần một số hỗ trợ từ khung, đặc biệt, mẫu khách truy cập có thể giúp việc này trở nên dễ dàng. Thật không may, nó không có sẵn ở đây.
Tôi đã thực hiện nó lấy cảm hứng từ LINQ ExpressionVisitor
để có cấu trúc tương tự như nó. Với điều này, bạn có thể áp dụng mẫu khách truy cập cho các đối tượng XML (LINQ-to-). (Tôi đã thực hiện thử nghiệm giới hạn về điều này nhưng nó hoạt động tốt theo như tôi có thể nói)
public abstract class XObjectVisitor
{
public virtual XObject Visit(XObject node)
{
if (node != null)
return node.Accept(this);
return node;
}
public ReadOnlyCollection<XObject> Visit(IEnumerable<XObject> nodes)
{
return nodes.Select(node => Visit(node))
.Where(node => node != null)
.ToList()
.AsReadOnly();
}
public T VisitAndConvert<T>(T node) where T : XObject
{
if (node != null)
return Visit(node) as T;
return node;
}
public ReadOnlyCollection<T> VisitAndConvert<T>(IEnumerable<T> nodes) where T : XObject
{
return nodes.Select(node => VisitAndConvert(node))
.Where(node => node != null)
.ToList()
.AsReadOnly();
}
protected virtual XObject VisitAttribute(XAttribute node)
{
return node.Update(node.Name, node.Value);
}
protected virtual XObject VisitComment(XComment node)
{
return node.Update(node.Value);
}
protected virtual XObject VisitDocument(XDocument node)
{
return node.Update(
node.Declaration,
VisitAndConvert(node.Nodes())
);
}
protected virtual XObject VisitElement(XElement node)
{
return node.Update(
node.Name,
VisitAndConvert(node.Attributes()),
VisitAndConvert(node.Nodes())
);
}
protected virtual XObject VisitDocumentType(XDocumentType node)
{
return node.Update(
node.Name,
node.PublicId,
node.SystemId,
node.InternalSubset
);
}
protected virtual XObject VisitProcessingInstruction(XProcessingInstruction node)
{
return node.Update(
node.Target,
node.Data
);
}
protected virtual XObject VisitText(XText node)
{
return node.Update(node.Value);
}
protected virtual XObject VisitCData(XCData node)
{
return node.Update(node.Value);
}
#region Implementation details
internal InternalAccessor Accessor
{
get { return new InternalAccessor(this); }
}
internal class InternalAccessor
{
private XObjectVisitor visitor;
internal InternalAccessor(XObjectVisitor visitor) { this.visitor = visitor; }
internal XObject VisitAttribute(XAttribute node) { return visitor.VisitAttribute(node); }
internal XObject VisitComment(XComment node) { return visitor.VisitComment(node); }
internal XObject VisitDocument(XDocument node) { return visitor.VisitDocument(node); }
internal XObject VisitElement(XElement node) { return visitor.VisitElement(node); }
internal XObject VisitDocumentType(XDocumentType node) { return visitor.VisitDocumentType(node); }
internal XObject VisitProcessingInstruction(XProcessingInstruction node) { return visitor.VisitProcessingInstruction(node); }
internal XObject VisitText(XText node) { return visitor.VisitText(node); }
internal XObject VisitCData(XCData node) { return visitor.VisitCData(node); }
}
#endregion
}
public static class XObjectVisitorExtensions
{
#region XObject.Accept "instance" method
public static XObject Accept(this XObject node, XObjectVisitor visitor)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(visitor, "visitor");
// yay, easy dynamic dispatch
Acceptor acceptor = new Acceptor(node as dynamic);
return acceptor.Accept(visitor);
}
private class Acceptor
{
public Acceptor(XAttribute node) : this(v => v.Accessor.VisitAttribute(node)) { }
public Acceptor(XComment node) : this(v => v.Accessor.VisitComment(node)) { }
public Acceptor(XDocument node) : this(v => v.Accessor.VisitDocument(node)) { }
public Acceptor(XElement node) : this(v => v.Accessor.VisitElement(node)) { }
public Acceptor(XDocumentType node) : this(v => v.Accessor.VisitDocumentType(node)) { }
public Acceptor(XProcessingInstruction node) : this(v => v.Accessor.VisitProcessingInstruction(node)) { }
public Acceptor(XText node) : this(v => v.Accessor.VisitText(node)) { }
public Acceptor(XCData node) : this(v => v.Accessor.VisitCData(node)) { }
private Func<XObjectVisitor, XObject> accept;
private Acceptor(Func<XObjectVisitor, XObject> accept) { this.accept = accept; }
public XObject Accept(XObjectVisitor visitor) { return accept(visitor); }
}
#endregion
#region XObject.Update "instance" method
public static XObject Update(this XAttribute node, XName name, string value)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");
Validation.CheckArgumentNull(value, "value");
return new XAttribute(name, value);
}
public static XObject Update(this XComment node, string value = null)
{
Validation.CheckNullReference(node);
return new XComment(value);
}
public static XObject Update(this XDocument node, XDeclaration declaration = null, params object[] content)
{
Validation.CheckNullReference(node);
return new XDocument(declaration, content);
}
public static XObject Update(this XElement node, XName name, params object[] content)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");
return new XElement(name, content);
}
public static XObject Update(this XDocumentType node, string name, string publicId = null, string systemId = null, string internalSubset = null)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(name, "name");
return new XDocumentType(name, publicId, systemId, internalSubset);
}
public static XObject Update(this XProcessingInstruction node, string target, string data)
{
Validation.CheckNullReference(node);
Validation.CheckArgumentNull(target, "target");
Validation.CheckArgumentNull(data, "data");
return new XProcessingInstruction(target, data);
}
public static XObject Update(this XText node, string value = null)
{
Validation.CheckNullReference(node);
return new XText(value);
}
public static XObject Update(this XCData node, string value = null)
{
Validation.CheckNullReference(node);
return new XCData(value);
}
#endregion
}
public static class Validation
{
public static void CheckNullReference<T>(T obj) where T : class
{
if (obj == null)
throw new NullReferenceException();
}
public static void CheckArgumentNull<T>(T obj, string paramName) where T : class
{
if (obj == null)
throw new ArgumentNullException(paramName);
}
}
ps, việc triển khai cụ thể này sử dụng một số tính năng .NET 4 để làm cho việc triển khai dễ dàng / sạch sẽ hơn một chút (sử dụng dynamic
và các đối số mặc định). Không quá khó để làm cho nó tương thích với .NET 3.5, thậm chí có thể tương thích với .NET 2.0.
Sau đó, để triển khai khách truy cập, đây là một cách tổng quát có thể thay đổi nhiều không gian tên (và tiền tố được sử dụng).
public class ChangeNamespaceVisitor : XObjectVisitor
{
private INamespaceMappingManager manager;
public ChangeNamespaceVisitor(INamespaceMappingManager manager)
{
Validation.CheckArgumentNull(manager, "manager");
this.manager = manager;
}
protected INamespaceMappingManager Manager { get { return manager; } }
private XName ChangeNamespace(XName name)
{
var mapping = Manager.GetMapping(name.Namespace);
return mapping.ChangeNamespace(name);
}
private XObject ChangeNamespaceDeclaration(XAttribute node)
{
var mapping = Manager.GetMapping(node.Value);
return mapping.ChangeNamespaceDeclaration(node);
}
protected override XObject VisitAttribute(XAttribute node)
{
if (node.IsNamespaceDeclaration)
return ChangeNamespaceDeclaration(node);
return node.Update(ChangeNamespace(node.Name), node.Value);
}
protected override XObject VisitElement(XElement node)
{
return node.Update(
ChangeNamespace(node.Name),
VisitAndConvert(node.Attributes()),
VisitAndConvert(node.Nodes())
);
}
}
// and all the gory implementation details
public class NamespaceMappingManager : INamespaceMappingManager
{
private Dictionary<XNamespace, INamespaceMapping> namespaces = new Dictionary<XNamespace, INamespaceMapping>();
public NamespaceMappingManager Add(XNamespace fromNs, XNamespace toNs, string toPrefix = null)
{
var item = new NamespaceMapping(fromNs, toNs, toPrefix);
namespaces.Add(item.FromNs, item);
return this;
}
public INamespaceMapping GetMapping(XNamespace fromNs)
{
INamespaceMapping mapping;
if (!namespaces.TryGetValue(fromNs, out mapping))
mapping = new NullMapping();
return mapping;
}
private class NullMapping : INamespaceMapping
{
public XName ChangeNamespace(XName name)
{
return name;
}
public XObject ChangeNamespaceDeclaration(XAttribute node)
{
return node.Update(node.Name, node.Value);
}
}
private class NamespaceMapping : INamespaceMapping
{
private XNamespace fromNs;
private XNamespace toNs;
private string toPrefix;
public NamespaceMapping(XNamespace fromNs, XNamespace toNs, string toPrefix = null)
{
this.fromNs = fromNs ?? "";
this.toNs = toNs ?? "";
this.toPrefix = toPrefix;
}
public XNamespace FromNs { get { return fromNs; } }
public XNamespace ToNs { get { return toNs; } }
public string ToPrefix { get { return toPrefix; } }
public XName ChangeNamespace(XName name)
{
return name.Namespace == fromNs
? toNs + name.LocalName
: name;
}
public XObject ChangeNamespaceDeclaration(XAttribute node)
{
if (node.Value == fromNs.NamespaceName)
{
if (toNs == XNamespace.None)
return null;
var xmlns = !String.IsNullOrWhiteSpace(toPrefix)
? (XNamespace.Xmlns + toPrefix)
: node.Name;
return node.Update(xmlns, toNs.NamespaceName);
}
return node.Update(node.Name, node.Value);
}
}
}
public interface INamespaceMappingManager
{
INamespaceMapping GetMapping(XNamespace fromNs);
}
public interface INamespaceMapping
{
XName ChangeNamespace(XName name);
XObject ChangeNamespaceDeclaration(XAttribute node);
}
Và một phương pháp trợ giúp nhỏ để bóng lăn:
T ChangeNamespace<T>(T node, XNamespace fromNs, XNamespace toNs, string toPrefix = null) where T : XObject
{
return node.Accept(
new ChangeNamespaceVisitor(
new NamespaceMappingManager()
.Add(fromNs, toNs, toPrefix)
)
) as T;
}
Sau đó, để loại bỏ một không gian tên, bạn có thể gọi nó như vậy:
var doc = ChangeNamespace(XDocument.Load(pathToXml),
fromNs: "http://schema.peters.com/doc_353/1/Types",
toNs: null);
Sử dụng khách truy cập này, bạn có thể viết một INamespaceMappingManager
để xóa tất cả các không gian tên.
T RemoveAllNamespaces<T>(T node) where T : XObject
{
return node.Accept(
new ChangeNamespaceVisitor(new RemoveNamespaceMappingManager())
) as T;
}
public class RemoveNamespaceMappingManager : INamespaceMappingManager
{
public INamespaceMapping GetMapping(XNamespace fromNs)
{
return new RemoveNamespaceMapping();
}
private class RemoveNamespaceMapping : INamespaceMapping
{
public XName ChangeNamespace(XName name)
{
return name.LocalName;
}
public XObject ChangeNamespaceDeclaration(XAttribute node)
{
return null;
}
}
}