2017-06-30 3 views
0

PHPのsimpleXmlを使用して、musicxmlファイルを画面上の楽譜として表示しています。スコアのどこかに音部記号の変更がない限り、すべてがうまくいきます。ここでは対策27で音部記号の変更を示すxmlファイルの抜粋です:PHPを使用してxml要素が他の要素との関連でどこにあるかを調べる

<measure number="27"> 
    <note> 
    </note> 
    <note> 
    </note> 
    <attributes> 
    <clef number="2"> 
     <sign>F</sign> 
     <line>4</line> 
     </clef> 
    </attributes> 
    <note> 
    </note> 
    <note> 
    </note> 
    <note> 
    </note> 
    </measure> 

私は($ノートとして$ measure->注)foreachのを経由して各小節の音符を通過してい。私はを使って小節の音節変化を検出することができます。(isset($ measure-> attributes-> clef))しかしこれは私にを教えていません。小節の変更が発生します。。この例では最後の3つの音、)

私はsimpleXmlIteratorに見えたが、このケースでSimpleXMLオブジェクトのうち抜粋($対策を処理していないようですが)私が試した:

$sxi = new SimpleXMLIterator($measure); 

これは私に以下の警告を与えました:

警告: にSimpleXMLElement :: __構築物():エンティティ:行29:パーサーエラー:I var_dump($measure)が、私はそれが最後で音部記号の変更を置くことを確認すると '<'、期待 タグを起動

が見つかりませんノート配列のが、音符の配列中のその適切な場所を決定するために使用することができる数を割り当て:

object(SimpleXMLElement)#21 (4) { 
    ["@attributes"]=> 
    array(2) { 
    ["number"]=> 
    string(2) "25" 
    ["width"]=> 
    string(6) "468.94" 
    } 
    ["note"]=> 
    array(25) { 
    ... 
    [20]=> 
    object(SimpleXMLElement)#43 (5) { 
     ["rest"]=> 
     object(SimpleXMLElement)#49 (0) { 
     } 
     ["duration"]=> 
     string(1) "4" 
    } 
    [21]=> 
    object(SimpleXMLElement)#45 (7) { 
     ["@attributes"]=> 
     array(2) { 
     ["default-x"]=> 
     string(6) "293.75" 
     ["default-y"]=> 
     string(7) "-255.00" 
     } 
     ["pitch"]=> 
     object(SimpleXMLElement)#49 (2) { 
     ["step"]=> 
     string(1) "D" 
     ["octave"]=> 
     string(1) "4" 
     } 
     ["duration"]=> 
     string(1) "2" 
    } 
    } 
    ["attributes"]=> 
    object(SimpleXMLElement)#44 (1) { 
    ["clef"]=> 
    object(SimpleXMLElement)#49 (3) { 
     ["@attributes"]=> 
     array(1) { 
     ["number"]=> 
     string(1) "2" 
     } 
     ["sign"]=> 
     string(1) "G" 
     ["line"]=> 
     string(1) "2" 
    } 
    } 
} 

オブジェクト#44(音部変化)A、実際に【2つの音の間に来なければなりません休符とメモ]はオブジェクト#43と#45です。だから、もし私がそれらの "オブジェクト番号"にアクセスする方法を理解できれば、私の問題は解決することができます。誰でもこれを行う方法、またはこの問題を解決するためのより良い方法を知っていますか?

答えて

0

、PHPの直接XPathを実行しますSimpleXMLElement::xpathpreceding-sibling::*およびfollowing-sibling::*を使用してください。以下はbefore_notesafter_notesのための2つの内の配列と小節番号でインデックス付け多次元配列、$音部記号を、作成します。

サンプルXML

$xml = simplexml_load_string(
'<score-partwise version="1.0"> 
    <part-list> 
    <score-part id="P1"> 
     <part-name>Voice</part-name> 
    </score-part> 
    </part-list> 
    <part id="P1"> 
    <measure number="26"> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
    </measure> 
    <measure number="27"> 
     <note/> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
     <note/> 
     <note/> 
     <note/> 
    </measure> 
    <measure number="28"> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
    </measure> 
    <measure number="29"> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <attributes> 
      <test>XSLT is cool!</test> 
     </attributes> 
     <note/> 
    </measure> 
    </part> 
</score-partwise>'); 

コードの解析

$clefs = [];  

foreach($xml->xpath('//measure') as $item) { 

    $measure_num = (integer)$item->xpath('@number')[0]; 

    $clefs['before_notes'][$measure_num] = count($item->xpath("attributes[clef!='']/preceding-sibling::note")); 
    $clefs['after_notes'][$measure_num] = count($item->xpath("attributes[clef!='']/following-sibling::note")); 

} 

echo var_dump($clefs); 
// array(2) { 
// ["before_notes"]=> array(4) { 
//  [26]=> int(5) [27]=> int(2) [28]=> int(1) [29]=> int(0) 
// } 
// ["after_notes"]=> array(4) { 
//  [26]=> int(0) [27]=> int(3) [28]=> int(4) [29]=> int(0) 
// } 
// } 
0

私のために働く解決策が見つかりました。私は、simpleXml()の戻り値がオブジェクト(XML文字列ではありません)であることに気付きました。したがって、その一部をsimpleXmlIterator()に渡すことはできません。

代わりに、私はきちんと$対策を処理することになった:

if(isset($measure->attributes->clef) && $measureNbr > 0) { 
    // There is a clef change in this measure! 
    $clefNbr = $measure->attributes->clef->attributes()->number; 
    $durationBeforeClefChg=0; 
    foreach($measure as $property => $value) { 
     // Count the unchorded durations before the clef change. 
      if(is_object($value) || is_array($value)) { 
       foreach($value as $p2 => $v2) { 
        if(trim($p2) == 'duration' 
         && $property == 'note' 
         && ((!property_exists($value,'staff')) 
          || trim($value->staff) == $clefNbr) 
         && !property_exists($value,'chord')) { 
          $durationBeforeClefChg+= intval(trim($v2)); 
        } 
        if(trim($p2) == 'clef' && $property == 'attributes') { 
         $newClef = trim($v2->sign); 
         break 2; 
        } 
       } 
      } 
    } 
} 
0

XSLT解決策を考えてみましょう:次のようにオブジェクトとして、そしてそれを反復します。この特別な目的のXML変換言語は、新しい個別XMLファイルの<attributes>の前後にある<notes>要素の数を取得するために、preceding-sibling::*およびfollowing-sibling::* XPathメソッドを実行して、要素の数を取得できます。

PHPは、php-xslクラスのXSLT 1.0スクリプトを実行できます。以下は、サンプルXMLレンダリングで特定の情報を出力する方法を示しています。このアプローチでは、foreachループを使用してifロジックをネストする必要はありません。

入力XML

<?xml version="1.0"?> 
<score-partwise version="1.0"> 
    <part-list> 
    <score-part id="P1"> 
     <part-name>Voice</part-name> 
    </score-part> 
    </part-list> 
    <part id="P1"> 
    <measure number="26"> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
    </measure> 
    <measure number="27"> 
     <note/> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
     <note/> 
     <note/> 
     <note/> 
    </measure> 
    <measure number="28"> 
     <note/> 
     <attributes> 
     <clef number="2"> 
      <sign>F</sign> 
      <line>4</line> 
     </clef> 
     </attributes> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
    </measure> 
    <measure number="29"> 
     <note/> 
     <note/> 
     <note/> 
     <note/> 
     <attributes> 
      <test>XSLT is cool!</test> 
     </attributes> 
     <note/> 
    </measure> 
    </part> 
</score-partwise> 

が(.xslファイルとして保存XSLTは、以下のいずれかのファイルでPHPで解析する(無音部と様々な位置における属性と一つと異なる小節番号)または文字列)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="measure"> 
     <xsl:copy> 
     <xsl:copy-of select="@*"/> 
     <notes_before_clef><xsl:copy-of select="count(attributes[clef!='']/preceding-sibling::note)"/></notes_before_clef> 
     <notes_after_clef><xsl:copy-of select="count(attributes[clef!='']/following-sibling::note)"/></notes_after_clef> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

PHP(.iniファイル内のphp-XSL拡張を有効)

// LOAD XML AND XSLT 
$xml = new DOMDocument; 
$xml->load('Input.xml');   // OR $xml->loadXML($xmlstring); 

$xsl = new DOMDocument; 
$xsl->load('XSLT_Script.xsl'); // OR $xsl->loadXML($xslstring); 

// INITIALIZE TRANSFORMER 
$proc = new XSLTProcessor; 
$proc->importStyleSheet($xsl);  

// TRANSFORM SOURCE TO NEW TREE 
$newXML = $proc->transformToXML($xml); 

// ECHO OUTPUT 
echo $newXML; 

// SAVE OUTPUT TO FILE 
file_put_contents('Output.xml', $newXML); 

出力XML代わり(最終用途のニーズのために、このファイルを解析)

<?xml version="1.0"?> 
<score-partwise version="1.0"> 
    <part-list> 
    <score-part id="P1"> 
     <part-name>Voice</part-name> 
    </score-part> 
    </part-list> 
    <part id="P1"> 
    <measure number="26"> 
     <notes_before_clef>5</notes_before_clef> 
     <notes_after_clef>0</notes_after_clef> 
    </measure> 
    <measure number="27"> 
     <notes_before_clef>2</notes_before_clef> 
     <notes_after_clef>3</notes_after_clef> 
    </measure> 
    <measure number="28"> 
     <notes_before_clef>1</notes_before_clef> 
     <notes_after_clef>4</notes_after_clef> 
    </measure> 
    <measure number="29"> 
     <notes_before_clef>0</notes_before_clef> 
     <notes_after_clef>0</notes_after_clef> 
    </measure> 
    </part> 
</score-partwise> 
+0

感謝、パフェ!これは、私のニーズにさらに便利な何かにXMLを事前にマッサージする新しい可能性を開きます。 – smmcroberts

関連する問題