2011-08-11 15 views
3

C#/ .NET 4.0アプリケーションでエンティティ名と値のリストを表示したいと考えています。C#でXMLエンティティの値を取得するにはどうすればよいですか?

XmlDocument.DocumentType.Entitiesを使用してエンティティ名を簡単に取得することはできますが、それらのエンティティの値を取得する良い方法はありますか?

InnerTextを使用してテキストのみのエンティティの値を取得できることに気付きましたが、これはXMLタグを含むエンティティでは機能しません。

正規表現に頼る最善の方法はありますか?

のは、私はこのような文書を持っているとしましょう:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE document [ 
    <!ENTITY test "<para>only a test</para>"> 
    <!ENTITY wwwc "World Wide Web Corporation"> 
    <!ENTITY copy "&#xA9;"> 
]> 

<document> 
    <!-- The following image is the World Wide Web Corporation logo. --> 
    <graphics image="logo" alternative="&wwwc; Logo"/> 
</document> 

私は3エンティティ名(テスト、wwwc、およびコピー)を含むユーザーにリストを提示したい、それらの値と一緒に(名前に続く引用符で囲まれたテキスト)。私は他のエンティティ内にネストされたエンティティの問題を考えなかったので、エンティティ値を完全に展開するか、引用符にあるようにテキストを表示するソリューションに興味があります。あなたがXmlDocumentオブジェクトを持っている場合は

+10

GAH! Regex for XMLを使用しないでください! – Josh

+0

xml構造を教えてください。 – Neelesh

+0

また、値を表示したいと言うと、再帰的に意味しますか?子ノードを持つエンティティがどのように表示されるのでしょうか? – Josh

答えて

2

最も洗練された解決策はありませんが、私の目的には十分にうまくいくと思われるものがありました。最初に、元のドキュメントを解析し、そのドキュメントからエンティティノードを取得しました。次に、小さなインメモリのXML文書を作成しました。そこにすべてのエンティティノードを追加しました。次に、一時XML内のすべてのエンティティへのエンティティ参照を追加しました。最後に、すべての参照からInnerXmlを取り出しました。

はここにいくつかのサンプルコードです:

 // parse the original document and retrieve its entities 
     XmlDocument parsedXmlDocument = new XmlDocument(); 
     XmlUrlResolver resolver = new XmlUrlResolver(); 
     resolver.Credentials = CredentialCache.DefaultCredentials; 
     parsedXmlDocument.XmlResolver = resolver; 
     parsedXmlDocument.Load(path); 

     // create a temporary xml document with all the entities and add references to them 
     // the references can then be used to retrieve the value for each entity 
     XmlDocument entitiesXmlDocument = new XmlDocument(); 
     XmlDeclaration dec = entitiesXmlDocument.CreateXmlDeclaration("1.0", null, null); 
     entitiesXmlDocument.AppendChild(dec); 
     XmlDocumentType newDocType = entitiesXmlDocument.CreateDocumentType(parsedXmlDocument.DocumentType.Name, parsedXmlDocument.DocumentType.PublicId, parsedXmlDocument.DocumentType.SystemId, parsedXmlDocument.DocumentType.InternalSubset); 
     entitiesXmlDocument.AppendChild(newDocType); 
     XmlElement root = entitiesXmlDocument.CreateElement("xmlEntitiesDoc"); 
     entitiesXmlDocument.AppendChild(root); 
     XmlNamedNodeMap entitiesMap = entitiesXmlDocument.DocumentType.Entities; 

     // build a dictionary of entity names and values 
     Dictionary<string, string> entitiesDictionary = new Dictionary<string, string>(); 
     for (int i = 0; i < entitiesMap.Count; i++) 
     { 
      XmlElement entityElement = entitiesXmlDocument.CreateElement(entitiesMap.Item(i).Name); 
      XmlEntityReference entityRefElement = entitiesXmlDocument.CreateEntityReference(entitiesMap.Item(i).Name); 
      entityElement.AppendChild(entityRefElement); 
      root.AppendChild(entityElement); 
      if (!string.IsNullOrEmpty(entityElement.ChildNodes[0].InnerXml)) 
      { 
       // do not add parameter entities or invalid entities 
       // this can be determined by checking for an empty string 
       entitiesDictionary.Add(entitiesMap.Item(i).Name, entityElement.ChildNodes[0].InnerXml); 
      } 
     } 
0

、おそらく再帰的に(XmlDocument.ChildNodesから)各XmlNodeオブジェクトをステップが容易になり、各ノードのためにあなたは、ノードの名前を取得するにはNameプロパティを使用することができます。 「値を取得する」は、XmlEntity/XmlAttribute/XmlTextにキャストできるXmlNodeオブジェクトへのプログラムによるアクセスの場合は、文字列表現の場合はInnerXml、文字列の場合はChildNodesです。

+0

これはノードではなく子ノードではないため、ノードにも到達しません(ただしノード、テキスト、属性、または組み合わせに解決される可能性があります)。解析の後、DTDエンティティは拡張された形式でDOMの不可欠な部分になり、その起点はもはや関連しません(ただし、Askerの場合は、この情報を必要とするユースケースがあります)。 – Abel

1

これは片道(未テスト)で、それはのXMLReaderと、このクラスののresolveEntity()メソッドを使用しています。

private Dictionary<string, string> GetEntities(XmlReader xr) 
{ 
    Dictionary<string, string> entityList = new Dictionary<string, string>(); 

    while (xr.Read()) 
    { 
     HandleNode(xr, entityList); 
    } 
    return entityList; 
} 

StringBuilder sbEntityResolver; 
int extElementIndex = 0; 
int resolveEntityNestLevel = -1; 
string dtdCurrentTopEntity = ""; 

private void HandleNode(XmlReader inReader, Dictionary<string, string> entityList) 
{ 
    if (inReader.NodeType == XmlNodeType.Element) 
    { 
     if (resolveEntityNestLevel < 0) 
     { 
       while (inReader.MoveToNextAttribute()) 
       { 
        HandleNode(inReader, entityList); // for namespaces 
        while (inReader.ReadAttributeValue()) 
        { 
         HandleNode(inReader, entityList); // recursive for resolving entity refs in attributes 
        }      
       } 
     } 
     else 
     { 
      extElementIndex++; 
      sbEntityResolver.Append(inReader.ReadOuterXml()); 
      resolveEntityNestLevel--; 
      if (!entityList.ContainsKey(dtdCurrentTopEntity)) 
      { 
       entityList.Add(dtdCurrentTopEntity, sbEntityResolver.ToString()); 
      } 
     } 
    } 
    else if (inReader.NodeType == XmlNodeType.EntityReference) 
    { 
     if (inReader.Name[0] != '#' && !entityList.ContainsKey(inReader.Name)) 
     { 
      if (resolveEntityNestLevel < 0) 
      { 
       sbEntityResolver = new StringBuilder(); // start building entity 
       dtdCurrentTopEntity = inReader.Name; 
      } 
      // entityReference can have contents that contains other 
      // entityReferences, so keep track of nest level 
      resolveEntityNestLevel++; 
      inReader.ResolveEntity(); 
     } 
    } 
    else if (inReader.NodeType == XmlNodeType.EndEntity) 
    { 
     resolveEntityNestLevel--; 
     if (resolveEntityNestLevel < 0) 
     { 
      if (!entityList.ContainsKey(dtdCurrentTopEntity)) 
      { 
       entityList.Add(dtdCurrentTopEntity, sbEntityResolver.ToString()); 
      } 
     } 
    } 
    else if (inReader.NodeType == XmlNodeType.Text) 
    { 
     if (resolveEntityNestLevel > -1) 
     { 
      sbEntityResolver.Append(inReader.Value); 
     } 
    } 
} 
+0

私は最終的にこの例でコードを使用することにはなりませんでしたが、解決策を思い付くために適切な行に沿って考えていました。特に、エンティティ参照からエンティティの値を取得するというアイデアは、キーであることが判明しました。 – Scott

+0

@Scott、あなたは解決策を得ています。完全性のために、答えとしてここに投稿することができます。あなた自身の質問に答えることは大丈夫です、それは他人を助けるかもしれません。サイドトラックで、XMLで参照されない値を取得するためにDTD自体を解析する必要がある場合、MindTouchのオープンソース[SGMLReader](http://developer.mindtouch.com/SgmlReader)にはSgmlParserがあります。役に立つかもしれないcs。 – pgfearo

0

あなたは簡単に、単純に再帰的にツリーを歩いて、XML文書の表現を表示することができます。

この小さなクラスはコンソールを使用しますが、必要に応じて簡単に変更できます。

public static class XmlPrinter { 
    private const Int32 SpacesPerIndent = 3; 

    public static void Print(XDocument xDocument) { 
     if (xDocument == null) { 
     Console.WriteLine("No XML Document Provided"); 
     return; 
     } 

     PrintElementRecursive(xDocument.Root); 
    } 

    private static void PrintElementRecursive(XElement element, Int32 indentationLevel = 0) { 
     if(element == null) return; 

     PrintIndentation(indentationLevel); 
     PrintElement(element); 
     PrintNewline(); 

     foreach (var xAttribute in element.Attributes()) { 
     PrintIndentation(indentationLevel + 1); 
     PrintAttribute(xAttribute); 
     PrintNewline(); 
     } 

     foreach (var xElement in element.Elements()) { 
     PrintElementRecursive(xElement, indentationLevel+1); 
     } 
    } 

    private static void PrintAttribute(XAttribute xAttribute) { 
     if (xAttribute == null) return; 

     Console.Write("[{0}] = \"{1}\"", xAttribute.Name, xAttribute.Value); 
    } 

    private static void PrintElement(XElement element) { 
     if (element == null) return; 

     Console.Write("{0}", element.Name); 

     if(!String.IsNullOrWhiteSpace(element.Value)) 
     Console.Write(" : {0}", element.Value); 
    } 

    private static void PrintIndentation(Int32 level) { 
     Console.Write(new String(' ', level * SpacesPerIndent)); 
    } 

    private static void PrintNewline() { 
     Console.Write(Environment.NewLine); 
    } 
} 

クラスを使用するのは簡単です。現在の設定ファイルを出力する例を次に示します。

static void Main(string[] args) { 
    XmlPrinter.Print(XDocument.Load(
     ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath 
         )); 

    Console.ReadKey(); 
} 

あなた自身で試してみて、あなたが望むものを手に入れることができるはずです。

関連する問題