Как правильно подготовить XML-файлы для базы данных на основе документов?

Как я могу переместить @attributes в корень этого элемента при преобразовании XML в JSON в PHP?

Самый простой способ, который я нашел для преобразования моего файла XML, это json_encode, Но я хотел бы избавиться от @attributes так id а также name например, будет прямо в корне, поскольку я буду импортировать все непосредственно в базу данных для манипуляций и вычислений. (Данные от 7 дней, чтобы умереть для любопытных).

Как я могу легко конвертировать эти XML-файлы в json, чтобы затем импортировать их в свою базу данных, не имея этих уродливых @attributes? Я понимаю их цель, но я не нуждаюсь в них здесь, как вы можете видеть.

Небольшой образец:

<block id="1" name="stone">
    <property name="Material" value="stone"/>
    <property name="Shape" value="Terrain"/>
    <property name="Mesh" value="terrain"/>
    <property name="Texture" value="1"/>
    <property name="ImposterExchange" value="imposterBlock" param1="97"/>
    <property name="DropScale" value="2"/>
    <property name="LPHardnessScale" value="2"/>
    <property name="Weight" value="125"/>
    <property name="Map.Color" value="100,100,100"/>
    <property class="RepairItems"> <property name="cobblestones" value="6"/> </property>
    <property name="HarvestOverdamage" value="true"/> <!-- default=true -->
    <drop event="Harvest" name="rockSmall" count="25" tool_category="harvestingTools"/>
    <drop event="Harvest" name="ironFragment" count="2" tool_category="harvestingTools"/>
    <drop event="Destroy" count="0"/>
    <drop event="Fall" name="rockSmall" count="50" prob="0.23" stick_chance="0"/>
    <drop event="Fall" name="ironFragment" count="4" prob="0.23" stick_chance="0"/>
    <drop event="Fall" name="destroyedStone" count="1" prob="0.1" stick_chance="0.5"/>
    <property name="CanMobsSpawnOn" value="true"/>
    <property name="EconomicValue" value="5"/>
    <property name="EconomicBundleSize" value="1"/>
    <property name="SellableToTrader" value="false"/>
</block>

Очень маленький, должен выглядеть как

{
    id: 1,
    name: "stone",
    property: [
        {
            name: "Material",
            value: "stone"
        },
        {
            name: "Shape",
            value: "Terrain"
        }
    ],
    drop: [
        {
            event: "Harverst",
            name: "rockSmall",
            count: 25
        },
        {
            event: "Harvest",
            name: "ironFragment",
            count: 2
        }
    ]
}

2 ответа

Решение

Давайте попробуем это:

<?php

$xmlContent = '<block id="1" name="stone">
    <property name="Material" value="stone"/>
    <property name="Shape" value="Terrain"/>
    <property name="Mesh" value="terrain"/>
    <property name="Texture" value="1"/>
    <property name="ImposterExchange" value="imposterBlock" param1="97"/>
    <property name="DropScale" value="2"/>
    <property name="LPHardnessScale" value="2"/>
    <property name="Weight" value="125"/>
    <property name="Map.Color" value="100,100,100"/>
    <property class="RepairItems"> <property name="cobblestones" value="6"/> </property>
    <property name="HarvestOverdamage" value="true"/> <!-- default=true -->
    <drop event="Harvest" name="rockSmall" count="25" tool_category="harvestingTools"/>
    <drop event="Harvest" name="ironFragment" count="2" tool_category="harvestingTools"/>
    <drop event="Destroy" count="0"/>
    <drop event="Fall" name="rockSmall" count="50" prob="0.23" stick_chance="0"/>
    <drop event="Fall" name="ironFragment" count="4" prob="0.23" stick_chance="0"/>
    <drop event="Fall" name="destroyedStone" count="1" prob="0.1" stick_chance="0.5"/>
    <property name="CanMobsSpawnOn" value="true"/>
    <property name="EconomicValue" value="5"/>
    <property name="EconomicBundleSize" value="1"/>
    <property name="SellableToTrader" value="false"/>
</block>';

$xml = simplexml_load_string($xmlContent, "SimpleXMLElement", LIBXML_NOCDATA);
$array = object_to_array($xml);
echo json_encode($array, JSON_PRETTY_PRINT);

function object_to_array($input)
{
    $return = array();
    foreach ((array)$input as $key => $value) {
        if (strpos($key, '@') === 0) {
            $key = substr($key, 1);
        }
        if (is_array($value) || is_object($value)) {
            $value = object_to_array($value);
        }

        $return[$key] = $value;
    }
    return $return;
}

Выход:

{
    "attributes": {
        "id": "1",
        "name": "stone"
    },
    "property": [
        {
            "attributes": {
                "name": "Material",
                "value": "stone"
            }
        },

Вместо того, чтобы рассматривать это как "преобразование произвольного XML-файла в JSON", следует рассматривать его как "извлечение необходимой информации из XML-файла" плюс "сериализацию этой информации в виде JSON".

Если файл всегда имеет структуру, которую вы показали, вы можете легко извлечь id а также name на верхнем уровне:

$block = simplexml_load_string($my_xml_data);
$extracted = [
    'id' => (int)$block['id'],
    'name' => (string)$block['name'],
];

Затем вы можете перебрать property элементы явно получают свое имя и значение:

$extracted['property'] = [];
foreach ( $block->property as $property ) {
    $extracted['property'][] = [
         'name' => (string)$property['name'],
         'value' => (string)$property['value']
    ];
}

Если вы хотите сделать это немного более общим, вы можете перебрать все атрибуты и захватить их имена и значения:

$extracted['property'] = [];
foreach ( $block->property as $property ) {
    $next_property = [];
    foreach ( $property->attributes() as $attr_name => $attr_value ) {
         $next_property[ $attr_name ] = (string)$attr_value;
    }
    $extracted['property'][] = $next_property;
}

Затем вы можете использовать то же самое, чтобы получить drop узлы, или вы можете сделать его действительно универсальным и использовать любые элементы верхнего уровня:

foreach ( $block->children() as $element_name => $element ) {
    // Note that unlike looping over an array, you will 
    //  get the same $element_name more than once!
    if ( ! isset($extracted[$element_name]) ) {
        $extracted[$element_name] = [];
    }

    $next_item = [];
    foreach ( $element->attributes() as $attr_name => $attr_value ) {
         $next_item[ $attr_name ] = (string)$attr_value;
    }
    $extracted[$element_name][] = $next_item;
}

Обратите внимание, что это не является рекурсивным, потому что очень сложно создать рекурсивную функцию, которая одновременно покрывает все возможные вводы XML и дает хороший результат. Предполагая, что общая форма обрабатываемого вами XML не изменится, вы можете решить, какую форму структуры данных (и, следовательно, JSON) вы хотите создать.

Другие вопросы по тегам