カテゴリー
C++ JUCE

JUCEでXmlファイルを扱う

juce::Fileクラスのインスタンスと、

const juce::File PresetManager::colorsFile { juce::File::getSpecialLocation(
  juce::File::SpecialLocationType::userApplicationDataDirectory)
  .getChildFile(ProjectInfo::companyName)
  .getChildFile(ProjectInfo::projectName)
  .getChildFile("UserSettings")
  .getChildFile("Colors.preset")
};

以下のような色の情報をhexで記録しているXmlファイルがあるとします。

<Colors>
  <Color quality="Major" hex="ffbe5050"/>
  <Color quality="Minor" hex="ffbe5050"/>
  <Color quality="minorMajor" hex="ffbe5050"/>
  <Color quality="Dominant" hex="ffbe5050"/>
</Colors>

このファイルをreadする場合は、以下のようにします。一番外側のタグ<Colors>は特に指定しなくても勝手にparseしてくれるようです。

std::unique_ptr<juce::XmlElement> colorsData;

void readXml() {
  colorsData = juce::XmlDocument::parse(colorsFile);
  for(auto* c: colorsData->getChildIterator()) {
    DBG(c->getStringAttribute("quality");
  }
}

// prints
// Major
// Minor
// minorMajor
// Dominant

二段構造のXmlの作成と読み込みは以下のコード例

if(!colorsFile.existsAsFile()) {
  // parent
  juce::XmlElement parentXml("DATA");
    
  // controls
  auto controlsXml = std::make_unique<juce::XmlElement ("Controls");
    
  auto childXmlSlider = std::make_unique<juce::XmlElement>("Color");
  childXmlSlider->setAttribute("control", SLIDER_COLOR);
  std::string trackColor = "fff28d11";
  childXmlSlider->setAttribute("hex", trackColor);

  // update with a method
  colorHandler.updateHex(SLIDER_COLOR, trackColor);
  controlsXml->addChildElement(childXmlSlider.release());
    
  // chords
  auto chordsXml = std::make_unique<juce::XmlElement>("Chords");
  auto childXmlMinor = std::make_unique<juce::XmlElement>("Color");
  childXmlMinor->setAttribute("quality", MINOR_NAME);
  std::string minorColor = "ff3488c3";
  childXmlMinor->setAttribute("hex", minorColor);
  colorHandler.updateHex(MINOR_NAME, minorColor);
  auto childXmlMinorMajor = std::make_unique<juce::XmlElement>("Color");
  childXmlMinorMajor->setAttribute("quality", MINOR_MAJOR_NAME);
  std::string minMajorColor = "ff4e7e4f";
  childXmlMinorMajor->setAttribute("hex", minMajorColor); // greenyellow
  colorHandler.updateHex(MINOR_MAJOR_NAME, minMajorColor);
  auto childXmlMajor = std::make_unique<juce::XmlElement>("Color");
  childXmlMajor->setAttribute("quality", MAJOR_NAME);
  std::string majorColor = "ffeeb42b";
  childXmlMajor->setAttribute("hex", majorColor); // orange
  colorHandler.updateHex(MAJOR_NAME, majorColor);
  auto childXmlDominant = std::make_unique<juce::XmlElement>("Color");
  std::string dominantColor = "ff7fa080";
  childXmlDominant->setAttribute("quality", DOMINANT_NAME);
  childXmlDominant->setAttribute("hex", dominantColor); // purple
  colorHandler.updateHex(DOMINANT_NAME, dominantColor);
  chordsXml->addChildElement(childXmlMinor.release());
  chordsXml->addChildElement(childXmlMinorMajor.release());
  chordsXml->addChildElement(childXmlMajor.release());
  chordsXml->addChildElement(childXmlDominant.release());
    
  parentXml.addChildElement(controlsXml.release());
  parentXml.addChildElement(chordsXml.release());
  if(!parentXml.writeTo(colorsFile)) {
    DBG("Failed to create Colors xml file");
  } 
} else {
  // if exists, read and update hex with it.
  // returns the outmost XmlElement*
  auto xml = juce::XmlDocument::parse(colorsFile);
  if(xml != nullptr) {
    for(auto child : xml->getChildIterator()) {
      if(child->hasTagName("Controls")) {
        for(auto control: child->getChildIterator()) {
          const auto name = control->getStringAttribute("control");
          const auto hex = control->getStringAttribute("hex");
          colorHandler.updateHex(name, hex);
        }
      }
      else if (child->hasTagName("Chords")) {
        for(auto chords: child->getChildIterator()) {
          const auto name = chords->getStringAttribute("quality");
          const auto hex = chords->getStringAttribute("hex");
          colorHandler.updateHex(name, hex);
        }
      }
    }
  }
}

voicingsFileというxmlファイル内に以下のデータがあるとします。

<DATA>
  <Voicings>
    <Voicing index="1" name="minor7 11 9top" quality="Minor">
      <Voice value="1"/>
      <Voice value="11"/>
      <Voice value="16"/>
      <Voice value="18"/>
      <Voice value="27"/>
    </Voicing>
    <Voicing index="2" name="minor7 11 b3top" quality="Minor">
      <Voice value="1"/>
      <Voice value="11"/>
      <Voice value="18"/>
      <Voice value="20"/>
      <Voice value="28"/>
    </Voicing>
    <Voicing index="3" name="minor7 11 11top" quality="Minor">
      <Voice value="1"/>
      <Voice value="11"/>
      <Voice value="16"/>
      <Voice value="27"/>
      <Voice value="30"/>
    </Voicing>
  </Voicings>
</DATA>
// auto xmlはTagName "DATA"のxmlElementのunique_ptrです。
auto xml = juce::XmlDocument::parse(voicingsFile);

// auto voicingsXmlはTagName "Voicings"のxmlElementのunique_ptrです。
auto voicingsXml = xml->getChildByName("Voicings");

// child elements からattributeで検索することができます。
auto voicingInInterest = voicingsXml->getChildByAttribute("index", std::to_string(index));

// 削除する場合はremoveChildElementを使います。
voicingsXml->removeChildElement(voicingInInterest, true);