3

xmlを逆シリアル化する方法に関するいくつかの投稿と記事をすでに読んでいますが、ニーズに合わせてコードを記述する方法がまだわかりません。したがって、xmlの逆シリアル化に関する別の質問をお詫びします))

デシリアライズする必要のある大きな(50 MB)xmlファイルがあります。xsd.exeを使用してドキュメントのxsdスキーマを取得し、プロジェクトに配置したc#クラスファイルを自動生成します。このxmlファイルからいくつかの(すべてではない)データを取得して、それをsqlデータベースに入れたいと思います。

ファイルの階層は次のとおりです(簡略化すると、xsdは非常に大きくなります)。

public class yml_catalog 
{
    public yml_catalogShop[] shop { /*realization*/ }
}

public class yml_catalogShop
{
    public yml_catalogShopOffersOffer[][] offers { /*realization*/ }
}

public class yml_catalogShopOffersOffer
{
    // here goes all the data (properties) I want to obtain ))
}

そしてここに私のコードがあります:

最初のアプローチ:

yml_catalogShopOffersOffer catalog;
var serializer = new XmlSerializer(typeof(yml_catalogShopOffersOffer));
var reader = new StreamReader(@"C:\div_kid.xml");
catalog = (yml_catalogShopOffersOffer) serializer.Deserialize(reader);//exception occures
reader.Close();

InvalidOperationExceptionが発生します:XML(3,2)ドキュメントにエラーがあります

2番目のアプローチ:

XmlSerializer ser = new XmlSerializer(typeof(yml_catalogShopOffersOffer));
yml_catalogShopOffersOffer result;
using (XmlReader reader = XmlReader.Create(@"C:\div_kid.xml"))          
{
    result = (yml_catalogShopOffersOffer)ser.Deserialize(reader); // exception occures
}

InvalidOperationException:XML(0,0)ドキュメントにエラーがあります

3番目:ファイル全体を逆シリアル化しようとしました:

 XmlSerializer ser = new XmlSerializer(typeof(yml_catalog)); // exception occures
 yml_catalog result;
 using (XmlReader reader = XmlReader.Create(@"C:\div_kid.xml"))          
 {
     result = (yml_catalog)ser.Deserialize(reader);
 }

そして、私は次のようになります:

error CS0030: The convertion of type "yml_catalogShopOffersOffer[]" into "yml_catalogShopOffersOffer" is not possible.

error CS0029: The implicit convertion of type "yml_catalogShopOffersOffer" into "yml_catalogShopOffersOffer[]" is not possible.

では、例外を取得しないようにコードを修正(または上書き)する方法は?

編集:私が書くときも:

XDocument doc = XDocument.Parse(@"C:\div_kid.xml");

XmlExceptionが発生します:ルートレベル、文字列1、位置1の許可されていないデータ。

xmlファイルの最初の文字列は次のとおりです。

<?xml version="1.0" encoding="windows-1251"?>

編集2: xmlファイルの短い例:

<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<yml_catalog date="2012-11-01 23:29">
<shop>
   <name>OZON.ru</name>
   <company>?????? "???????????????? ??????????????"</company>
   <url>http://www.ozon.ru/</url>
   <currencies>
     <currency id="RUR" rate="1" />
   </currencies>
   <categories>
      <category id=""1126233>base category</category>
      <category id="1127479" parentId="1126233">bla bla bla</category>
      // here goes all the categories
   </categories>
   <offers>
      <offer>
         <price></price>
         <picture></picture>
      </offer>
      // other offers
   </offers>
</shop>
</yml_catalog>

PS 私はすでに答えを受け入れました(それは完璧です)。ただし、categoryIdを使用して、各オファーの「基本カテゴリ」を見つける必要があります。データは階層的であり、基本カテゴリは「parentId」属性を持たないカテゴリです。そこで、「基本カテゴリ」を見つけるための再帰的なメソッドを作成しましたが、それが終了することはありません。algorythmはそれほど速くないようです))これ
が私のコードです:( main()メソッド内)

var doc = XDocument.Load(@"C:\div_kid.xml");
var offers = doc.Descendants("shop").Elements("offers").Elements("offer");
foreach (var offer in offers.Take(2))
        {
            var category = GetCategory(categoryId, doc);
            // here goes other code
        }

ヘルパーメソッド:

public static string GetCategory(int categoryId, XDocument document)
    {
        var tempId = categoryId;
            var categories = document.Descendants("shop").Elements("categories").Elements("category");
            foreach (var category in categories)
            {
                if (category.Attribute("id").ToString() == categoryId.ToString())
                {
                    if (category.Attributes().Count() == 1)
                    {
                        return category.ToString();
                    }
                    tempId = Convert.ToInt32(category.Attribute("parentId"));
                }
            }
        return GetCategory(tempId, document);
    }

このような状況で再帰を使用できますか?そうでない場合、他にどのようにして「基本カテゴリ」を見つけることができますか?

4

1 に答える 1

7

LINQ to XML を試してみてください。XElement result = XElement.Load(@"C:\div_kid.xml");

LINQ でのクエリは素晴らしいものですが、最初は少し奇妙です。SQL のような構文で、またはラムダ式を使用してドキュメントからノードを選択します。次に、関心のあるデータを含む匿名オブジェクトを作成 (または既存のクラスを使用) します。

実際に見てみるのが一番です。

サンプル XML とコードに基づいて、具体的な例を次に示します。

var element = XElement.Load(@"C:\div_kid.xml");
var shopsQuery =
    from shop in element.Descendants("shop")
    select new
    {
        Name = (string) shop.Descendants("name").FirstOrDefault(),
        Company = (string) shop.Descendants("company").FirstOrDefault(),
        Categories = 
            from category in shop.Descendants("category")
            select new {
                Id = category.Attribute("id").Value,
                Parent = category.Attribute("parentId").Value,
                Name = category.Value
            },
        Offers =
            from offer in shop.Descendants("offer")
            select new { 
                Price = (string) offer.Descendants("price").FirstOrDefault(),
                Picture = (string) offer.Descendants("picture").FirstOrDefault()
            }

    };

foreach (var shop in shopsQuery){
    Console.WriteLine(shop.Name);
    Console.WriteLine(shop.Company);
    foreach (var category in shop.Categories)
    {
        Console.WriteLine(category.Name);
        Console.WriteLine(category.Id);
    }
    foreach (var offer in shop.Offers)
    {
        Console.WriteLine(offer.Price);
        Console.WriteLine(offer.Picture);
    }
}  

おまけとして:フラットcategory要素からカテゴリのツリーを逆シリアル化する方法は次のとおりです。Children のリストには次のタイプが必要なため、それらを格納するには適切なクラスが必要です。

class Category
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public List<Category> Children { get; set; }
    public IEnumerable<Category> Descendants {
        get
        {
            return (from child in Children
                    select child.Descendants).SelectMany(x => x).
                    Concat(new Category[] { this });
        }
    }
}

ドキュメント内のすべてのカテゴリを含むリストを作成するには:

var categories = (from category in element.Descendants("category")
                    orderby int.Parse( category.Attribute("id").Value )
                    select new Category()
                    {
                        Id = int.Parse(category.Attribute("id").Value),
                        ParentId = category.Attribute("parentId") == null ?
                            null as int? : int.Parse(category.Attribute("parentId").Value),
                        Children = new List<Category>()
                    }).Distinct().ToList();

次に、それらをツリーに編成します (フラット リストから階層に大きく借用されます)。

var lookup = categories.ToLookup(cat => cat.ParentId);
foreach (var category in categories)
{
    category.Children = lookup[category.Id].ToList();
}
var rootCategories = lookup[null].ToList();

を含むルートを見つけるにはtheCategory:

var root = (from cat in rootCategories
            where cat.Descendants.Contains(theCategory)
            select cat).FirstOrDefault();
于 2013-01-26T21:18:13.297 に答える