* Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* Description: OBS XML construction and parsing utility implementation.
*/
#include "datasystem/common/l2cache/obs_client/obs_xml_util.h"
#include <sstream>
namespace datasystem {
std::string ObsXmlUtil::BuildBatchDeleteXml(const std::vector<std::string> &keys)
{
std::ostringstream oss;
oss << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Delete>\n<Quiet>true</Quiet>\n";
for (const auto &key : keys) {
oss << " <Object><Key>" << key << "</Key></Object>\n";
}
oss << "</Delete>";
return oss.str();
}
std::string ObsXmlUtil::BuildCompleteMultipartXml(const std::vector<std::pair<int, std::string>> &parts)
{
std::ostringstream oss;
oss << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<CompleteMultipartUpload>\n";
for (const auto &part : parts) {
oss << " <Part><PartNumber>" << part.first << "</PartNumber><ETag>" << part.second
<< "</ETag></Part>\n";
}
oss << "</CompleteMultipartUpload>";
return oss.str();
}
std::string ObsXmlUtil::GetXmlElement(const std::string &xml, const std::string &tag)
{
std::string openTag = "<" + tag + ">";
std::string closeTag = "</" + tag + ">";
auto start = xml.find(openTag);
if (start == std::string::npos) {
return "";
}
start += openTag.size();
auto end = xml.find(closeTag, start);
if (end == std::string::npos) {
return "";
}
return xml.substr(start, end - start);
}
std::vector<std::string> ObsXmlUtil::GetXmlElements(const std::string &xml, const std::string &tag)
{
std::vector<std::string> results;
std::string openTag = "<" + tag + ">";
std::string closeTag = "</" + tag + ">";
size_t pos = 0;
while (pos < xml.size()) {
auto start = xml.find(openTag, pos);
if (start == std::string::npos) {
break;
}
start += openTag.size();
auto end = xml.find(closeTag, start);
if (end == std::string::npos) {
break;
}
results.push_back(xml.substr(start, end - start));
pos = end + closeTag.size();
}
return results;
}
std::string ObsXmlUtil::ParseInitiateMultipartResponse(const std::string &xml)
{
return GetXmlElement(xml, "UploadId");
}
Status ObsXmlUtil::ParseListObjectsResponse(const std::string &xml, std::vector<std::string> &keys,
std::vector<uint64_t> &sizes, bool &isTruncated,
std::string &nextMarker)
{
keys.clear();
sizes.clear();
isTruncated = false;
nextMarker.clear();
std::string truncated = GetXmlElement(xml, "IsTruncated");
isTruncated = (truncated == "true");
nextMarker = GetXmlElement(xml, "NextMarker");
std::string openTag = "<Contents>";
std::string closeTag = "</Contents>";
size_t pos = 0;
while (pos < xml.size()) {
auto start = xml.find(openTag, pos);
if (start == std::string::npos) {
break;
}
auto end = xml.find(closeTag, start);
if (end == std::string::npos) {
break;
}
std::string content = xml.substr(start, end + closeTag.size() - start);
std::string key = GetXmlElement(content, "Key");
std::string sizeStr = GetXmlElement(content, "Size");
if (!key.empty()) {
keys.push_back(key);
sizes.push_back(sizeStr.empty() ? 0 : std::stoull(sizeStr));
}
pos = end + closeTag.size();
}
if (isTruncated && nextMarker.empty() && !keys.empty()) {
nextMarker = keys.back();
}
return Status::OK();
}
void ObsXmlUtil::ParseErrorResponse(const std::string &xml, std::string &code, std::string &message)
{
code = GetXmlElement(xml, "Code");
message = GetXmlElement(xml, "Message");
}
}