简体   繁体   中英

How can I properly prepare XML files for document-based database?

How can I move @attributes to the root of that element when converting XML to JSON in PHP?

The simplest way I found to convert my XML file is to json_encode . But I would like to get rid of the @attributes so id and name for example would be directly at the root as I'll be importing everything directly into a database for manipulation and computation. (Data are from 7 days to die for the curious).

How can I easily convert those XML files to json for I can then import those into my database without having those ugly @attributes? I understand their purpose but I have no need for them here, as you can see.

Small sample:

<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>

Very small should-look-like:

{
    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
        }
    ]
}

Let's try this:

<?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;
}

Output:

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

Rather than looking at this as "convert an arbitrary XML file to JSON", look at it as "extract the information I want from an XML file" plus "serialise that information as JSON".

If the file always has the structure you showed, you can easily extract the id and name at the top level:

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

You can then loop over the property elements explicitly getting their name and value:

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

If you want to make this a bit more generic, you can loop over all the attributes and capture their names and values:

$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;
}

You can then use the same thing to get the drop nodes, or you can make it really generic and slurp in whatever top-level elements you get:

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;
}

Note that this is not recursive, because it is very tricky to make a recursive function that both covers all possible XML inputs and produces a nice result. By assuming the general shape of the XML you're processing won't change, you can decide what shape of data structure (and therefore JSON) you want to create.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM