Search code examples
c#xmllinq.net-3.5morelinq

Morelinq ExceptBy using several specific element


There are 2 xml files

First xml file contains:

<Prices>
    <Price>
        <SalesOrg>700</SalesOrg>
        <AreaOfPricing>D20</AreaOfPricing>
        <ProductId>20228090</ProductId>
        <EffectiveDate>2015-05-11T00:00:00+7</EffectiveDate>
        <DistributorPriceFibrate>200</DistributorPriceFibrate>
        <CustomerPriceFibrate>20</CustomerPriceFibrate>
        <CustomerPriceInDozen>30</CustomerPriceInDozen>
        <CustomerPriceinPC>80.00</CustomerPriceinPC>
        <CompanyID>001</CompanyID>
        <ValidTo>2999-12-31T00:00:00+7</ValidTo>
        <UOM>CS</UOM>
        <Currency>IDR</Currency>
    </Price>
<Price>
        <SalesOrg>700</SalesOrg>
        <AreaOfPricing>D20</AreaOfPricing>
        <ProductId>20228090</ProductId>
        <EffectiveDate>2015-05-11T00:00:00+7</EffectiveDate>
        <DistributorPriceFibrate>200</DistributorPriceFibrate>
        <CustomerPriceFibrate>20</CustomerPriceFibrate>
        <CustomerPriceInDozen>30</CustomerPriceInDozen>
        <CustomerPriceinPC>80.00</CustomerPriceinPC>
        <CompanyID>001</CompanyID>
        <ValidTo>2999-12-31T00:00:00+7</ValidTo>
        <UOM>CS</UOM>
        <Currency>IDR</Currency>
    </Price>
<Price>
        <SalesOrg>700</SalesOrg>
        <AreaOfPricing>D20</AreaOfPricing>
        <ProductId>20228090</ProductId>
        <EffectiveDate>2015-05-11T00:00:00+7</EffectiveDate>
        <DistributorPriceFibrate>180</DistributorPriceFibrate>
        <CustomerPriceFibrate>20</CustomerPriceFibrate>
        <CustomerPriceInDozen>30</CustomerPriceInDozen>
        <CustomerPriceinPC>80.00</CustomerPriceinPC>
        <CompanyID>001</CompanyID>
        <ValidTo>2999-12-31T00:00:00+7</ValidTo>
        <UOM>CS</UOM>
        <Currency>IDR</Currency>
    </Price>
</Prices>

and the second xml file:

<Prices>
    <Price>
        <SalesOrg>700</SalesOrg>
        <AreaOfPricing>D20</AreaOfPricing>
        <ProductId>20228090</ProductId>
        <EffectiveDate>2015-05-11T00:00:00+7</EffectiveDate>
        <DistributorPriceFibrate>200</DistributorPriceFibrate>
        <CustomerPriceFibrate>20</CustomerPriceFibrate>
        <CustomerPriceInDozen>30</CustomerPriceInDozen>
        <CustomerPriceinPC>80.00</CustomerPriceinPC>
        <CompanyID>001</CompanyID>
        <ValidTo>2999-12-31T00:00:00+7</ValidTo>
        <UOM>CS</UOM>
        <Currency>IDR</Currency>
    </Price>
</Prices>

What I want is, using morelinq features ExceptBy(), or using custom class extend IEqualityComparer on Except() features in Linq to return something like this (between 1st xml file and the 2nd xml file, even when the third tag price on 1st xml file have different DistributorPriceFibrate value):

<Prices/>

Since Except() compares all values on element 'Price' node, I just want compare only specific element at <ProductId> and <EffectiveDate> only.

If they are the same, then go empty tag <Prices/>. If not same value on those elements, return the price tag from 1st xml file which not have same value ProductID and EffectiveDate from 2nd xml file.

What I've done I distinct the 1st xml file:

var distinctItemsonxmldoc1 =
                xmldoc1
                .Descendants("Price")
                .DistinctBy(element => new
                {
                    ProductId = (string)element.Element("ProductId"),
                    EffectiveDate = (string)element.Element("EffectiveDate")
                });
var afterdistinctxmldoc1 = new XElement("Prices");
            foreach (var a in distinctItemsonxmldoc1 )
            {
                afterdistinctxmldoc1.Add(a);
            }

and when using except to compare between 2 files:

var afterexcept = afterdistinctxmldoc1.Descendants("Price").Cast<XNode>().Except(xmldoc2.Descendants("Price").Cast<XNode>(), new XNodeEqualityComparer());

but it compare all element value on price node. how using ExceptBy() in spesific element? or custom IComparer maybe?

Thanks before.

EDIT
already solved. see the answer by @dbc.


Solution

  • To confirm I understand your question: given two XML documents, you want to enumerate through instances of each Price element in the first document with distinct values values for the child elements ProductId and EffectiveDate, skipping all those whose ProductId and EffectiveDate match a Price element in the second document, using MoreLinq.

    In that case, you can do:

            var diff = xmldoc1.Descendants("Price").ExceptBy(xmldoc2.Descendants("Price"),
                e => new { ProductId = e.Elements("ProductId").Select(p => p.Value).FirstOrDefault(), EffectiveDate = e.Elements("EffectiveDate").Select(p => p.Value).FirstOrDefault() });