0

Been banging my head against the wall trying to figure this out. Just trying to put together a script that will eventually link together various components of these game files (like for a wiki). I'm sure this is a simple matter of not doing the deferencing right but I can't figure it out for the life of me.

Here's a sample of the XML:

 <?xml version="1.0" encoding="UTF-8"?>
<items>
        <!-- ???????????????????????????????????????????????????????????????????????????????  -->
    <!-- ?????????? Die Till Day 7 ?????????? Guns (Gun Parts) ?????????????????????????  -->
    <!-- ???????????????????????????????????????????????????????????????????????????????  -->
        <item id="1" name="pistol">
        <property name="Meshfile" value="Items/Weapons/Ranged/Pistol/PistolPrefab" />
        <property name="Material" value="metal" />
        <property name="HoldType" value="1" />
        <property name="Stacknumber" value="1" />
        <property name="RepairTools" value="repairKit,repairKit2ndGeneration,repairKit3rdGeneration" />
        <property name="Degradation" value="175" param1="false" />
        <property name="DegradationBreaksAfter" value="false" />
        <property name="SoundJammed" value="Weapons/weapon_jam" />
                <property name="Attachments" value="flashlight02" />
        <property class="Parts">
            <property name="Stock" value="pistol_grip" />
            <property name="Receiver" value="pistol_receiver" />
            <property name="Pump" value="pistol_parts" />
            <property name="Barrel" value="pistol_barrel" />
        </property>
        <property class="Action0">
            <property name="Class" value="Ranged" />
            <property name="Delay" value="0.15" />
            <property name="Range" value="120" />
            <property name="DamageBlock" value="1" />
            <property name="Magazine_size" value="15" />
            <property name="Magazine_items" value="9mmBullet, incendiary9mmBullet" />
            <property name="Reload_time" value="2" />
            <property name="Bullet_icon" value="pistol" />
            <property name="Sound_start" value="Weapons/Ranged/Pistol/pistol_fire" />
            <property name="Sound_repeat" value="" />
            <property name="Sound_end" value="" />
            <property name="Sound_empty" value="Weapons/weapon_empty" />
            <property name="Sound_reload" value="Weapons/Ranged/Pistol/pistol_reload" />
            <property name="Particles_muzzle_fire" value="nozzleflash" />
            <property name="Particles_muzzle_smoke" value="nozzlesmoke" />
            <property name="DamageBonus.head" value="6" />
            <property name="DamageBonus.glass" value="25" />
        </property>
        <property class="Action1">
            <property name="Class" value="Zoom" />
            <property name="Zoom_max_out" value="35" />
            <property name="Zoom_max_in" value="35" />
        </property>
                <property name="LightSource" value="lightSource" />
                <property name="ActivateObject" value="Attachments/flashlight/lightSource" /> 
                <property name="AttachmentFlashlight" value="flashlight02" />
        <property name="Group" value="Ammo/Weapons" />
        <property class="Preview">
            <property name="Zoom" value="14" />
            <property name="Pos" value="0.1,-0.1" />
            <property name="Rot" value="0,-45,0" />
        </property>
    </item>
</items>

Here is the actual program

    #!/usr/local/bin/perl
use warnings;
#use strict;
use XML::Simple qw(:strict);
use Data::Dumper;

my $xml = new XML::Simple;

my $data = $xml->XMLin(undef, KeyAttr => { item => 'name' }, ForceArray => ['item',  'property', 'property']);
#my $data = XMLin ($xml,  forcearray => [ qw (item property property) ], keyattr = +> [] );



#print Dumper($data);

#my @itemlist = @{$data->{name}};
#print Dumper( \@itemlist );

my $items = $data->{item};

for my $item_name (keys %$items) {
my $item = $items->{$item_name};
my $item_id = $item->{id};
my $props = $item->{property};

print "Name:  " . $item_name . " ID:  " . $item_id;

for my $prop (@$props)
{
my $prop_name = $prop->{name};
my $prop_value = $prop->{value};

print $prop_name ":  " . $prop_value . "\n";
}

}

Really just looking for a few things like how do I access the actual item name (in this case pistol) from a foreach and how would i get down to a property or the property of a property. I'm pretty sure I'm just screwing up what's a hash vs an array or not setting items right but I haven't found any examples that are close enough to my xml data to figure it out.

(Data Dumper gives me this)

$VAR1 = {
          'item' => {
                    'pistol' => {
                                'id' => '1',
                                'property' => [
                                              {
                                                'value' => 'Items/Weapons/Ranged/Pistol/PistolPrefab',
                                                'name' => 'Meshfile'
                                              },
                                              {
                                                'value' => 'metal',
                                                'name' => 'Material'
                                              },
                                              {
                                                'value' => '1',
                                                'name' => 'HoldType'
                                              },
                                              {
                                                'value' => '1',
                                                'name' => 'Stacknumber'
                                              },
                                              {
                                                'value' => 'repairKit,repairKit2ndGeneration,repairKit3rdGeneration',
                                                'name' => 'RepairTools'
                                              },
                                              {
                                                'value' => '175',
                                                'name' => 'Degradation',
                                                'param1' => 'false'
                                              },
                                                     etc

Errors:

No more errors!

2
  • 1
    See also: Why is XML::Simple “Discouraged”? Commented Nov 11, 2015 at 18:25
  • Give us some sample XML, and we can give you a better answer. Being able to use XML based constructs mean the code is way less of a headache. Commented Nov 11, 2015 at 19:28

2 Answers 2

1
my $items = $data->{item};
for my $item_name (keys %$items) {
   my $item = $items->{$item_name};
   my $item_id = $item->{id};
   my $props = $item->{property};
   for my $prop (@$props) {
      my $prop_name = $prop->{name};
      my $prop_value = $prop->{value};
      ...
   }
}

You've since updated to show that some properties look like:

{
    'name' => '...'
    'value' => '...',
}

and some look like

{
    'class' => '...',
    'property' => [ ... ],
}

but your code only handles the first kind. You probably want something like if ($prop->{class}).


print $prop_name ":  " . $prop_value . "\n";

treats $prop_name as a file handle. It should be

print $prop_name . ":  " . $prop_value . "\n";
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you so much. I actually read through several of your previous answers while trying to figure this out. I'm sure I can backtrack what you did to figure out how to get to the id or a property value. Many thanks!
I added a simple print on $name $id and $props. $name works but I get use of uninitialized value for the lines using id and props and for the print statement for id and prompts. I assumed props would be another hash or array reference right with potentially another array/hash inside it?
You appear to have changed something seeing as what I had posted didn't even compile. I removed the extraneous $, and now it runs fine (tested).
This is what I put in -- pastebin.com/yj9kAPM4D The first two work fine the second two give unitialized values and a bunch of open filehandle errors pastebin.com/BrviDSy7 Thanks again for your help. What's the best thing to look up for a tutorial on this? It seems like it's mixed across hashes and references.
skullCapBrown doesn't exist anywhere in the XML, so you couldn't possibly have gotten that output.
|
0

This doesn't directly answer your question, but I'm hoping to show an alternative approach - I'm a firm believer that XML::Simple... isn't. It makes your code far more complex than it needs to be. So a few examples on parsing your XML:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

#parse the file    
my $twig = XML::Twig -> new(); 
   $twig -> parsefile ( 'sample.xml' );

#iterate 'item' elements - note '//' means anywhere in document. 
#you can use './' to denote only things under the current node. 
foreach my $item ( $twig -> get_xpath ('//item') ) {

    #item attributes:
    print "ID: ", $item -> att('id'),"\n";
    print "name: ", $item -> att('name'),"\n";

    #subelement with a specific attribute name:
    print "Material: ", $item -> get_xpath("./property[\@name='Material']", 0) -> att('value'),"\n";

    #grab a nested property:
    print "Action0 Range:";
    #nb - errors on item 2, because it doesn't exist. This is illustrative. 
    print $item -> get_xpath(q{./property[@class='Action0']/property[@name='Range']},0) -> att('value'),"\n";


    #iterate children:
    print "Properties:\n";
    #note - children only iterates beneath current node. 
    foreach my $property ( $item -> children('property') ) {
        print $property -> att('name'),"\n" if defined $property->att('name');
    }

}

3 Comments

I saw Twig and really wanted to use it but unfortunately the server host I have for my site doesn't have Twig or Lib:XML so been stuck w/ XML simple. I'm going to open a ticket with them to try to install it. Not sure how long it will take but will definitely try your code once they put it in.
It's often in default repositories. I've got it on yum install on my Linux builds. But I really think it's worth doing - XML::Simple ... actually isn't much better than using regular expressions to parse
I tried this and it's much easier to read what's going on. Thank you for showing this alternate method

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.