Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse nested XML in Dart / Flutter

I need help parsing a nested XML file in Dart/Flutter.

I have a Dart class

class Genre {
String id;
String title;
String token;
String type;
// For children objets I want to get list of IDs
// as String
List<String> subGenres;
}

The XML file looks as follow:

<genres>
  <genre id="5003" title="Бизнес-книги" type="root">
    <genre id="5049" title="Банковское дело" token="bankovskoe_delo" type="genre"/>
    <genre id="5047" title="Кадровый менеджмент" token="kadrovyj_menedzhment" type="container">
      <genre id="5334" title="Аттестация персонала" token="attestaciya_personala" type="genre"/>
      <genre id="5330" title="Гендерные различия" token="gendernyye_razlichiya" type="genre"/>
      <genre id="5332" title="Конфликты" token="konflikty" type="genre"/>
    </genre>
  </genre>
  <genre id="5013" title="Юмористическая литература" type="root">
    <genre id="5201" title="Анекдоты" token="anekdoty" type="genre"/>
    <genre id="5202" title="Зарубежный юмор" token="zarubezhnyy" type="genre"/>
  </genre>
</genres>

So I have maybe unlimited levels of sub-genres. If a type of object is 'root' or 'container' it contains sub-genres, type 'genre' is a single record (like folders and files).

How can I get the list of all of my objects and parse it to class objects ?

like image 952
Igor Karelin Avatar asked May 11 '26 06:05

Igor Karelin


1 Answers

Use the xml package. I like to use a factory constructor that takes an XmlElement. There's no reason to limit yourself to a List<String> for the children; you could have a List<Genre> to build a tree that mirrors your XML. (You could, of course, do that instead if you prefer.)

Notice how the factory constructor iterates through any child XML elements, recursively calling itself. The normal constructor is private, so that only the factory constructor uses it. If a genre has no sub-genres, the subGenres list isEmpty.

import 'package:xml/xml.dart';

var sample = '''<?xml version="1.0" encoding="UTF-8"?>
<genres>
  <genre id="5003" title="Бизнес-книги" type="root">
    <genre id="5049" title="Банковское дело" token="bankovskoe_delo" type="genre"/>
    <genre id="5047" title="Кадровый менеджмент" token="kadrovyj_menedzhment" type="container">
      <genre id="5334" title="Аттестация персонала" token="attestaciya_personala" type="genre"/>
      <genre id="5330" title="Гендерные различия" token="gendernyye_razlichiya" type="genre"/>
      <genre id="5332" title="Конфликты" token="konflikty" type="genre"/>
    </genre>
  </genre>
  <genre id="5013" title="Юмористическая литература" type="root">
    <genre id="5201" title="Анекдоты" token="anekdoty" type="genre"/>
    <genre id="5202" title="Зарубежный юмор" token="zarubezhnyy" type="genre"/>
  </genre>
</genres>
''';

class Genre {
  Genre._(this.id, this.title, this.token, this.type, this.subGenres);

  factory Genre.fromElement(XmlElement genreElement) {
    return Genre._(
      genreElement.getAttribute('id'),
      genreElement.getAttribute('title'),
      genreElement.getAttribute('token'),
      genreElement.getAttribute('type'),
      genreElement
          .findElements('genre')
          .map<Genre>((e) => Genre.fromElement(e))
          .toList(),
    );
  }

  String id;
  String title;
  String token;
  String type;
  List<Genre> subGenres;
}

void main() {
  var root = XmlDocument.parse(sample).getElement('genres');
  var rootGenres = root
      .findElements('genre')
      .map<Genre>((e) => Genre.fromElement(e))
      .toList();

  print(rootGenres);
}
like image 182
Richard Heap Avatar answered May 13 '26 00:05

Richard Heap



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!